From 25dfa2c0e14d6f51b382ace5b9e1d3c42f73d7b1 Mon Sep 17 00:00:00 2001 From: jos Date: Sat, 13 Aug 2016 21:15:52 +0200 Subject: [PATCH] Put new build script into place, update unit testing, changed license to MIT, merged css and image into the bundled js file --- .babelrc | 3 + LICENSE | 189 +- dist/img/jsoneditor-icons.svg | 893 - dist/jsoneditor-minimalist.js | 9701 +---- dist/jsoneditor-minimalist.js.map | 1 + dist/jsoneditor-minimalist.map | 1 - dist/jsoneditor-minimalist.min.js | 35 - dist/jsoneditor.css | 929 - dist/jsoneditor.js | 36327 +----------------- dist/jsoneditor.js.map | 1 + dist/jsoneditor.map | 1 - dist/jsoneditor.min.css | 1 - dist/jsoneditor.min.js | 49 - dist/which files do I need.md | 25 +- examples/01_basic_usage.html | 1 - examples/02_viewer.html | 1 - examples/03_switch_mode.html | 1 - examples/04_load_and_save.html | 1 - examples/05_custom_fields_editable.html | 1 - examples/06_custom_styling.html | 1 - examples/07_json_schema_validation.html | 1 - examples/requirejs_demo/requirejs_demo.html | 1 - gulpfile.js | 112 +- package.json | 48 +- src/develop.html | 3 +- src/header.js | 29 + src/index.js | 13 +- src/{jsoneditor.css => jsoneditor.less} | 0 src/utils/immutabilityHelpers.js | 166 + test/immutabilityHelpers.test.js | 273 + test/{util.test.js => util.test.bak.js} | 0 31 files changed, 564 insertions(+), 48244 deletions(-) create mode 100755 .babelrc delete mode 100644 dist/img/jsoneditor-icons.svg create mode 100644 dist/jsoneditor-minimalist.js.map delete mode 100644 dist/jsoneditor-minimalist.map delete mode 100644 dist/jsoneditor-minimalist.min.js delete mode 100644 dist/jsoneditor.css create mode 100644 dist/jsoneditor.js.map delete mode 100644 dist/jsoneditor.map delete mode 100644 dist/jsoneditor.min.css delete mode 100644 dist/jsoneditor.min.js create mode 100644 src/header.js rename src/{jsoneditor.css => jsoneditor.less} (100%) create mode 100644 src/utils/immutabilityHelpers.js create mode 100644 test/immutabilityHelpers.test.js rename test/{util.test.js => util.test.bak.js} (100%) diff --git a/.babelrc b/.babelrc new file mode 100755 index 0000000..c13c5f6 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/LICENSE b/LICENSE index ea2712c..5eca8dd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,176 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +The MIT License (MIT) - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Copyright (c) 2016 Jos de Jong - 1. Definitions. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dist/img/jsoneditor-icons.svg b/dist/img/jsoneditor-icons.svg deleted file mode 100644 index 1b40068..0000000 --- a/dist/img/jsoneditor-icons.svg +++ /dev/null @@ -1,893 +0,0 @@ - - - JSON Editor Icons - - - - image/svg+xml - - JSON Editor Icons - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dist/jsoneditor-minimalist.js b/dist/jsoneditor-minimalist.js index a5159cf..87e29d4 100644 --- a/dist/jsoneditor-minimalist.js +++ b/dist/jsoneditor-minimalist.js @@ -24,9699 +24,10 @@ * Copyright (c) 2011-2016 Jos de Jong, http://jsoneditoronline.org * * @author Jos de Jong, - * @version 5.5.6 - * @date 2016-06-15 + * @version 6.0.0-BETA + * @date 2016-08-13 */ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define([], factory); - else if(typeof exports === 'object') - exports["JSONEditor"] = factory(); - else - root["JSONEditor"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var Ajv; - try { - Ajv = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module \"ajv\""); e.code = 'MODULE_NOT_FOUND'; throw e; }())); - } - catch (err) { - // no problem... when we need Ajv we will throw a neat exception - } - - var treemode = __webpack_require__(1); - var textmode = __webpack_require__(12); - var util = __webpack_require__(4); - - /** - * @constructor JSONEditor - * @param {Element} container Container element - * @param {Object} [options] Object with options. available options: - * {String} mode Editor mode. Available values: - * 'tree' (default), 'view', - * 'form', 'text', and 'code'. - * {function} onChange Callback method, triggered - * on change of contents - * {function} onError Callback method, triggered - * when an error occurs - * {Boolean} search Enable search box. - * True by default - * Only applicable for modes - * 'tree', 'view', and 'form' - * {Boolean} history Enable history (undo/redo). - * True by default - * Only applicable for modes - * 'tree', 'view', and 'form' - * {String} name Field name for the root node. - * Only applicable for modes - * 'tree', 'view', and 'form' - * {Number} indentation Number of indentation - * spaces. 4 by default. - * Only applicable for - * modes 'text' and 'code' - * {boolean} escapeUnicode If true, unicode - * characters are escaped. - * false by default. - * {boolean} sortObjectKeys If true, object keys are - * sorted before display. - * false by default. - * @param {Object | undefined} json JSON object - */ - function JSONEditor (container, options, json) { - if (!(this instanceof JSONEditor)) { - throw new Error('JSONEditor constructor called without "new".'); - } - - // check for unsupported browser (IE8 and older) - var ieVersion = util.getInternetExplorerVersion(); - if (ieVersion != -1 && ieVersion < 9) { - throw new Error('Unsupported browser, IE9 or newer required. ' + - 'Please install the newest version of your browser.'); - } - - if (options) { - // check for deprecated options - if (options.error) { - console.warn('Option "error" has been renamed to "onError"'); - options.onError = options.error; - delete options.error; - } - if (options.change) { - console.warn('Option "change" has been renamed to "onChange"'); - options.onChange = options.change; - delete options.change; - } - if (options.editable) { - console.warn('Option "editable" has been renamed to "onEditable"'); - options.onEditable = options.editable; - delete options.editable; - } - - // validate options - if (options) { - var VALID_OPTIONS = [ - 'ace', 'theme', - 'ajv', 'schema', - 'onChange', 'onEditable', 'onError', 'onModeChange', - 'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', 'sortObjectKeys' - ]; - - Object.keys(options).forEach(function (option) { - if (VALID_OPTIONS.indexOf(option) === -1) { - console.warn('Unknown option "' + option + '". This option will be ignored'); - } - }); - } - } - - if (arguments.length) { - this._create(container, options, json); - } - } - - /** - * Configuration for all registered modes. Example: - * { - * tree: { - * mixin: TreeEditor, - * data: 'json' - * }, - * text: { - * mixin: TextEditor, - * data: 'text' - * } - * } - * - * @type { Object. } - */ - JSONEditor.modes = {}; - - // debounce interval for JSON schema vaidation in milliseconds - JSONEditor.prototype.DEBOUNCE_INTERVAL = 150; - - /** - * Create the JSONEditor - * @param {Element} container Container element - * @param {Object} [options] See description in constructor - * @param {Object | undefined} json JSON object - * @private - */ - JSONEditor.prototype._create = function (container, options, json) { - this.container = container; - this.options = options || {}; - this.json = json || {}; - - var mode = this.options.mode || 'tree'; - this.setMode(mode); - }; - - /** - * Destroy the editor. Clean up DOM, event listeners, and web workers. - */ - JSONEditor.prototype.destroy = function () {}; - - /** - * Set JSON object in editor - * @param {Object | undefined} json JSON data - */ - JSONEditor.prototype.set = function (json) { - this.json = json; - }; - - /** - * Get JSON from the editor - * @returns {Object} json - */ - JSONEditor.prototype.get = function () { - return this.json; - }; - - /** - * Set string containing JSON for the editor - * @param {String | undefined} jsonText - */ - JSONEditor.prototype.setText = function (jsonText) { - this.json = util.parse(jsonText); - }; - - /** - * Get stringified JSON contents from the editor - * @returns {String} jsonText - */ - JSONEditor.prototype.getText = function () { - return JSON.stringify(this.json); - }; - - /** - * Set a field name for the root node. - * @param {String | undefined} name - */ - JSONEditor.prototype.setName = function (name) { - if (!this.options) { - this.options = {}; - } - this.options.name = name; - }; - - /** - * Get the field name for the root node. - * @return {String | undefined} name - */ - JSONEditor.prototype.getName = function () { - return this.options && this.options.name; - }; - - /** - * Change the mode of the editor. - * JSONEditor will be extended with all methods needed for the chosen mode. - * @param {String} mode Available modes: 'tree' (default), 'view', 'form', - * 'text', and 'code'. - */ - JSONEditor.prototype.setMode = function (mode) { - var container = this.container; - var options = util.extend({}, this.options); - var oldMode = options.mode; - var data; - var name; - - options.mode = mode; - var config = JSONEditor.modes[mode]; - if (config) { - try { - var asText = (config.data == 'text'); - name = this.getName(); - data = this[asText ? 'getText' : 'get'](); // get text or json - - this.destroy(); - util.clear(this); - util.extend(this, config.mixin); - this.create(container, options); - - this.setName(name); - this[asText ? 'setText' : 'set'](data); // set text or json - - if (typeof config.load === 'function') { - try { - config.load.call(this); - } - catch (err) { - console.error(err); - } - } - - if (typeof options.onModeChange === 'function' && mode !== oldMode) { - try { - options.onModeChange(mode, oldMode); - } - catch (err) { - console.error(err); - } - } - } - catch (err) { - this._onError(err); - } - } - else { - throw new Error('Unknown mode "' + options.mode + '"'); - } - }; - - /** - * Get the current mode - * @return {string} - */ - JSONEditor.prototype.getMode = function () { - return this.options.mode; - }; - - /** - * Throw an error. If an error callback is configured in options.error, this - * callback will be invoked. Else, a regular error is thrown. - * @param {Error} err - * @private - */ - JSONEditor.prototype._onError = function(err) { - if (this.options && typeof this.options.onError === 'function') { - this.options.onError(err); - } - else { - throw err; - } - }; - - /** - * Set a JSON schema for validation of the JSON object. - * To remove the schema, call JSONEditor.setSchema(null) - * @param {Object | null} schema - */ - JSONEditor.prototype.setSchema = function (schema) { - // compile a JSON schema validator if a JSON schema is provided - if (schema) { - var ajv; - try { - // grab ajv from options if provided, else create a new instance - ajv = this.options.ajv || Ajv({ allErrors: true, verbose: true }); - - } - catch (err) { - console.warn('Failed to create an instance of Ajv, JSON Schema validation is not available. Please use a JSONEditor bundle including Ajv, or pass an instance of Ajv as via the configuration option `ajv`.'); - } - - if (ajv) { - this.validateSchema = ajv.compile(schema); - - // add schema to the options, so that when switching to an other mode, - // the set schema is not lost - this.options.schema = schema; - - // validate now - this.validate(); - } - - this.refresh(); // update DOM - } - else { - // remove current schema - this.validateSchema = null; - this.options.schema = null; - this.validate(); // to clear current error messages - this.refresh(); // update DOM - } - }; - - /** - * Validate current JSON object against the configured JSON schema - * Throws an exception when no JSON schema is configured - */ - JSONEditor.prototype.validate = function () { - // must be implemented by treemode and textmode - }; - - /** - * Refresh the rendered contents - */ - JSONEditor.prototype.refresh = function () { - // can be implemented by treemode and textmode - }; - - /** - * Register a plugin with one ore multiple modes for the JSON Editor. - * - * A mode is described as an object with properties: - * - * - `mode: String` The name of the mode. - * - `mixin: Object` An object containing the mixin functions which - * will be added to the JSONEditor. Must contain functions - * create, get, getText, set, and setText. May have - * additional functions. - * When the JSONEditor switches to a mixin, all mixin - * functions are added to the JSONEditor, and then - * the function `create(container, options)` is executed. - * - `data: 'text' | 'json'` The type of data that will be used to load the mixin. - * - `[load: function]` An optional function called after the mixin - * has been loaded. - * - * @param {Object | Array} mode A mode object or an array with multiple mode objects. - */ - JSONEditor.registerMode = function (mode) { - var i, prop; - - if (util.isArray(mode)) { - // multiple modes - for (i = 0; i < mode.length; i++) { - JSONEditor.registerMode(mode[i]); - } - } - else { - // validate the new mode - if (!('mode' in mode)) throw new Error('Property "mode" missing'); - if (!('mixin' in mode)) throw new Error('Property "mixin" missing'); - if (!('data' in mode)) throw new Error('Property "data" missing'); - var name = mode.mode; - if (name in JSONEditor.modes) { - throw new Error('Mode "' + name + '" already registered'); - } - - // validate the mixin - if (typeof mode.mixin.create !== 'function') { - throw new Error('Required function "create" missing on mixin'); - } - var reserved = ['setMode', 'registerMode', 'modes']; - for (i = 0; i < reserved.length; i++) { - prop = reserved[i]; - if (prop in mode.mixin) { - throw new Error('Reserved property "' + prop + '" not allowed in mixin'); - } - } - - JSONEditor.modes[name] = mode; - } - }; - - // register tree and text modes - JSONEditor.registerMode(treemode); - JSONEditor.registerMode(textmode); - - module.exports = JSONEditor; - - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - - var Highlighter = __webpack_require__(2); - var History = __webpack_require__(3); - var SearchBox = __webpack_require__(6); - var ContextMenu = __webpack_require__(7); - var Node = __webpack_require__(8); - var ModeSwitcher = __webpack_require__(11); - var util = __webpack_require__(4); - - // create a mixin with the functions for tree mode - var treemode = {}; - - /** - * Create a tree editor - * @param {Element} container Container element - * @param {Object} [options] Object with options. available options: - * {String} mode Editor mode. Available values: - * 'tree' (default), 'view', - * and 'form'. - * {Boolean} search Enable search box. - * True by default - * {Boolean} history Enable history (undo/redo). - * True by default - * {function} onChange Callback method, triggered - * on change of contents - * {String} name Field name for the root node. - * {boolean} escapeUnicode If true, unicode - * characters are escaped. - * false by default. - * {Object} schema A JSON Schema for validation - * @private - */ - treemode.create = function (container, options) { - if (!container) { - throw new Error('No container element provided.'); - } - this.container = container; - this.dom = {}; - this.highlighter = new Highlighter(); - this.selection = undefined; // will hold the last input selection - this.multiselection = { - nodes: [] - }; - this.validateSchema = null; // will be set in .setSchema(schema) - this.errorNodes = []; - - this.node = null; - this.focusTarget = null; - - this._setOptions(options); - - if (this.options.history && this.options.mode !== 'view') { - this.history = new History(this); - } - - this._createFrame(); - this._createTable(); - }; - - /** - * Destroy the editor. Clean up DOM, event listeners, and web workers. - */ - treemode.destroy = function () { - if (this.frame && this.container && this.frame.parentNode == this.container) { - this.container.removeChild(this.frame); - this.frame = null; - } - this.container = null; - - this.dom = null; - - this.clear(); - this.node = null; - this.focusTarget = null; - this.selection = null; - this.multiselection = null; - this.errorNodes = null; - this.validateSchema = null; - this._debouncedValidate = null; - - if (this.history) { - this.history.destroy(); - this.history = null; - } - - if (this.searchBox) { - this.searchBox.destroy(); - this.searchBox = null; - } - - if (this.modeSwitcher) { - this.modeSwitcher.destroy(); - this.modeSwitcher = null; - } - }; - - /** - * Initialize and set default options - * @param {Object} [options] See description in constructor - * @private - */ - treemode._setOptions = function (options) { - this.options = { - search: true, - history: true, - mode: 'tree', - name: undefined, // field name of root node - schema: null - }; - - // copy all options - if (options) { - for (var prop in options) { - if (options.hasOwnProperty(prop)) { - this.options[prop] = options[prop]; - } - } - } - - // compile a JSON schema validator if a JSON schema is provided - this.setSchema(this.options.schema); - - // create a debounced validate function - this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL); - }; - - /** - * Set JSON object in editor - * @param {Object | undefined} json JSON data - * @param {String} [name] Optional field name for the root node. - * Can also be set using setName(name). - */ - treemode.set = function (json, name) { - // adjust field name for root node - if (name) { - // TODO: deprecated since version 2.2.0. Cleanup some day. - console.warn('Second parameter "name" is deprecated. Use setName(name) instead.'); - this.options.name = name; - } - - // verify if json is valid JSON, ignore when a function - if (json instanceof Function || (json === undefined)) { - this.clear(); - } - else { - this.content.removeChild(this.table); // Take the table offline - - // replace the root node - var params = { - field: this.options.name, - value: json - }; - var node = new Node(this, params); - this._setRoot(node); - - // validate JSON schema (if configured) - this.validate(); - - // expand - var recurse = false; - this.node.expand(recurse); - - this.content.appendChild(this.table); // Put the table online again - } - - // TODO: maintain history, store last state and previous document - if (this.history) { - this.history.clear(); - } - - // clear search - if (this.searchBox) { - this.searchBox.clear(); - } - }; - - /** - * Get JSON object from editor - * @return {Object | undefined} json - */ - treemode.get = function () { - // remove focus from currently edited node - if (this.focusTarget) { - var node = Node.getNodeFromTarget(this.focusTarget); - if (node) { - node.blur(); - } - } - - if (this.node) { - return this.node.getValue(); - } - else { - return undefined; - } - }; - - /** - * Get the text contents of the editor - * @return {String} jsonText - */ - treemode.getText = function() { - return JSON.stringify(this.get()); - }; - - /** - * Set the text contents of the editor - * @param {String} jsonText - */ - treemode.setText = function(jsonText) { - this.set(util.parse(jsonText)); - }; - - /** - * Set a field name for the root node. - * @param {String | undefined} name - */ - treemode.setName = function (name) { - this.options.name = name; - if (this.node) { - this.node.updateField(this.options.name); - } - }; - - /** - * Get the field name for the root node. - * @return {String | undefined} name - */ - treemode.getName = function () { - return this.options.name; - }; - - /** - * Set focus to the editor. Focus will be set to: - * - the first editable field or value, or else - * - to the expand button of the root node, or else - * - to the context menu button of the root node, or else - * - to the first button in the top menu - */ - treemode.focus = function () { - var input = this.content.querySelector('[contenteditable=true]'); - if (input) { - input.focus(); - } - else if (this.node.dom.expand) { - this.node.dom.expand.focus(); - } - else if (this.node.dom.menu) { - this.node.dom.menu.focus(); - } - else { - // focus to the first button in the menu - input = this.frame.querySelector('button'); - if (input) { - input.focus(); - } - } - }; - - /** - * Remove the root node from the editor - */ - treemode.clear = function () { - if (this.node) { - this.node.collapse(); - this.tbody.removeChild(this.node.getDom()); - delete this.node; - } - }; - - /** - * Set the root node for the json editor - * @param {Node} node - * @private - */ - treemode._setRoot = function (node) { - this.clear(); - - this.node = node; - - // append to the dom - this.tbody.appendChild(node.getDom()); - }; - - /** - * Search text in all nodes - * The nodes will be expanded when the text is found one of its childs, - * else it will be collapsed. Searches are case insensitive. - * @param {String} text - * @return {Object[]} results Array with nodes containing the search results - * The result objects contains fields: - * - {Node} node, - * - {String} elem the dom element name where - * the result is found ('field' or - * 'value') - */ - treemode.search = function (text) { - var results; - if (this.node) { - this.content.removeChild(this.table); // Take the table offline - results = this.node.search(text); - this.content.appendChild(this.table); // Put the table online again - } - else { - results = []; - } - - return results; - }; - - /** - * Expand all nodes - */ - treemode.expandAll = function () { - if (this.node) { - this.content.removeChild(this.table); // Take the table offline - this.node.expand(); - this.content.appendChild(this.table); // Put the table online again - } - }; - - /** - * Collapse all nodes - */ - treemode.collapseAll = function () { - if (this.node) { - this.content.removeChild(this.table); // Take the table offline - this.node.collapse(); - this.content.appendChild(this.table); // Put the table online again - } - }; - - /** - * The method onChange is called whenever a field or value is changed, created, - * deleted, duplicated, etc. - * @param {String} action Change action. Available values: "editField", - * "editValue", "changeType", "appendNode", - * "removeNode", "duplicateNode", "moveNode", "expand", - * "collapse". - * @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. - * @private - */ - treemode._onAction = function (action, params) { - // add an action to the history - if (this.history) { - this.history.add(action, params); - } - - this._onChange(); - }; - - /** - * Handle a change: - * - Validate JSON schema - * - Send a callback to the onChange listener if provided - * @private - */ - treemode._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); - } - } - }; - - /** - * Validate current JSON object against the configured JSON schema - * Throws an exception when no JSON schema is configured - */ - treemode.validate = function () { - // clear all current errors - if (this.errorNodes) { - this.errorNodes.forEach(function (node) { - node.setError(null); - }); - } - - var root = this.node; - if (!root) { // TODO: this should be redundant but is needed on mode switch - return; - } - - // check for duplicate keys - var duplicateErrors = root.validate(); - - // validate the JSON - var schemaErrors = []; - if (this.validateSchema) { - var valid = this.validateSchema(root.getValue()); - if (!valid) { - // apply all new errors - schemaErrors = this.validateSchema.errors - .map(function (error) { - return util.improveSchemaError(error); - }) - .map(function findNode (error) { - return { - node: root.findNode(error.dataPath), - error: error - } - }) - .filter(function hasNode (entry) { - return entry.node != null - }); - } - } - - // display the error in the nodes with a problem - this.errorNodes = duplicateErrors - .concat(schemaErrors) - .reduce(function expandParents (all, entry) { - // expand parents, then merge such that parents come first and - // original entries last - return entry.node - .findParents() - .map(function (parent) { - return { - node: parent, - child: entry.node, - error: { - message: parent.type === 'object' - ? 'Contains invalid properties' // object - : 'Contains invalid items' // array - } - }; - }) - .concat(all, [entry]); - }, []) - // TODO: dedupe the parent nodes - .map(function setError (entry) { - entry.node.setError(entry.error, entry.child); - return entry.node; - }); - }; - - /** - * Refresh the rendered contents - */ - treemode.refresh = function () { - if (this.node) { - this.node.updateDom({recurse: true}); - } - }; - - /** - * Start autoscrolling when given mouse position is above the top of the - * editor contents, or below the bottom. - * @param {Number} mouseY Absolute mouse position in pixels - */ - treemode.startAutoScroll = function (mouseY) { - var me = this; - var content = this.content; - var top = util.getAbsoluteTop(content); - var height = content.clientHeight; - var bottom = top + height; - var margin = 24; - var interval = 50; // ms - - if ((mouseY < top + margin) && content.scrollTop > 0) { - this.autoScrollStep = ((top + margin) - mouseY) / 3; - } - else if (mouseY > bottom - margin && - height + content.scrollTop < content.scrollHeight) { - this.autoScrollStep = ((bottom - margin) - mouseY) / 3; - } - else { - this.autoScrollStep = undefined; - } - - if (this.autoScrollStep) { - if (!this.autoScrollTimer) { - this.autoScrollTimer = setInterval(function () { - if (me.autoScrollStep) { - content.scrollTop -= me.autoScrollStep; - } - else { - me.stopAutoScroll(); - } - }, interval); - } - } - else { - this.stopAutoScroll(); - } - }; - - /** - * Stop auto scrolling. Only applicable when scrolling - */ - treemode.stopAutoScroll = function () { - if (this.autoScrollTimer) { - clearTimeout(this.autoScrollTimer); - delete this.autoScrollTimer; - } - if (this.autoScrollStep) { - delete this.autoScrollStep; - } - }; - - - /** - * Set the focus to an element in the editor, set text selection, and - * set scroll position. - * @param {Object} selection An object containing fields: - * {Element | undefined} dom The dom element - * which has focus - * {Range | TextRange} range A text selection - * {Node[]} nodes Nodes in case of multi selection - * {Number} scrollTop Scroll position - */ - treemode.setSelection = function (selection) { - if (!selection) { - return; - } - - if ('scrollTop' in selection && this.content) { - // TODO: animated scroll - this.content.scrollTop = selection.scrollTop; - } - if (selection.nodes) { - // multi-select - this.select(selection.nodes); - } - if (selection.range) { - util.setSelectionOffset(selection.range); - } - if (selection.dom) { - selection.dom.focus(); - } - }; - - /** - * Get the current focus - * @return {Object} selection An object containing fields: - * {Element | undefined} dom The dom element - * which has focus - * {Range | TextRange} range A text selection - * {Node[]} nodes Nodes in case of multi selection - * {Number} scrollTop Scroll position - */ - treemode.getSelection = function () { - var range = util.getSelectionOffset(); - if (range && range.container.nodeName !== 'DIV') { // filter on (editable) divs) - range = null; - } - - return { - dom: this.focusTarget, - range: range, - nodes: this.multiselection.nodes.slice(0), - scrollTop: this.content ? this.content.scrollTop : 0 - }; - }; - - /** - * Adjust the scroll position such that given top position is shown at 1/4 - * of the window height. - * @param {Number} top - * @param {function(boolean)} [callback] Callback, executed when animation is - * finished. The callback returns true - * when animation is finished, or false - * when not. - */ - treemode.scrollTo = function (top, callback) { - var content = this.content; - if (content) { - var editor = this; - // cancel any running animation - if (editor.animateTimeout) { - clearTimeout(editor.animateTimeout); - delete editor.animateTimeout; - } - if (editor.animateCallback) { - editor.animateCallback(false); - delete editor.animateCallback; - } - - // calculate final scroll position - var height = content.clientHeight; - var bottom = content.scrollHeight - height; - var finalScrollTop = Math.min(Math.max(top - height / 4, 0), bottom); - - // animate towards the new scroll position - var animate = function () { - var scrollTop = content.scrollTop; - var diff = (finalScrollTop - scrollTop); - if (Math.abs(diff) > 3) { - content.scrollTop += diff / 3; - editor.animateCallback = callback; - editor.animateTimeout = setTimeout(animate, 50); - } - else { - // finished - if (callback) { - callback(true); - } - content.scrollTop = finalScrollTop; - delete editor.animateTimeout; - delete editor.animateCallback; - } - }; - animate(); - } - else { - if (callback) { - callback(false); - } - } - }; - - /** - * Create main frame - * @private - */ - treemode._createFrame = function () { - // create the frame - this.frame = document.createElement('div'); - this.frame.className = 'jsoneditor jsoneditor-mode-' + this.options.mode; - this.container.appendChild(this.frame); - - // create one global event listener to handle all events from all nodes - var editor = this; - function onEvent(event) { - // when switching to mode "code" or "text" via the menu, some events - // are still fired whilst the _onEvent methods is already removed. - if (editor._onEvent) { - editor._onEvent(event); - } - } - this.frame.onclick = function (event) { - var target = event.target;// || event.srcElement; - - onEvent(event); - - // prevent default submit action of buttons when editor is located - // inside a form - if (target.nodeName == 'BUTTON') { - event.preventDefault(); - } - }; - this.frame.oninput = onEvent; - this.frame.onchange = onEvent; - this.frame.onkeydown = onEvent; - this.frame.onkeyup = onEvent; - this.frame.oncut = onEvent; - this.frame.onpaste = onEvent; - this.frame.onmousedown = onEvent; - this.frame.onmouseup = onEvent; - this.frame.onmouseover = onEvent; - this.frame.onmouseout = onEvent; - // Note: focus and blur events do not propagate, therefore they defined - // using an eventListener with useCapture=true - // see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html - util.addEventListener(this.frame, 'focus', onEvent, true); - util.addEventListener(this.frame, 'blur', onEvent, true); - this.frame.onfocusin = onEvent; // for IE - this.frame.onfocusout = onEvent; // for IE - - // create menu - this.menu = document.createElement('div'); - this.menu.className = 'jsoneditor-menu'; - this.frame.appendChild(this.menu); - - // create expand all button - var expandAll = document.createElement('button'); - expandAll.className = 'jsoneditor-expand-all'; - expandAll.title = 'Expand all fields'; - expandAll.onclick = function () { - editor.expandAll(); - }; - this.menu.appendChild(expandAll); - - // create expand all button - var collapseAll = document.createElement('button'); - collapseAll.title = 'Collapse all fields'; - collapseAll.className = 'jsoneditor-collapse-all'; - collapseAll.onclick = function () { - editor.collapseAll(); - }; - this.menu.appendChild(collapseAll); - - // create undo/redo buttons - if (this.history) { - // create undo button - var undo = document.createElement('button'); - undo.className = 'jsoneditor-undo jsoneditor-separator'; - undo.title = 'Undo last action (Ctrl+Z)'; - undo.onclick = function () { - editor._onUndo(); - }; - this.menu.appendChild(undo); - this.dom.undo = undo; - - // create redo button - var redo = document.createElement('button'); - redo.className = 'jsoneditor-redo'; - redo.title = 'Redo (Ctrl+Shift+Z)'; - redo.onclick = function () { - editor._onRedo(); - }; - this.menu.appendChild(redo); - this.dom.redo = redo; - - // register handler for onchange of history - this.history.onChange = function () { - undo.disabled = !editor.history.canUndo(); - redo.disabled = !editor.history.canRedo(); - }; - this.history.onChange(); - } - - // create mode box - if (this.options && this.options.modes && this.options.modes.length) { - var me = this; - this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) { - me.modeSwitcher.destroy(); - - // switch mode and restore focus - me.setMode(mode); - me.modeSwitcher.focus(); - }); - } - - // create search box - if (this.options.search) { - this.searchBox = new SearchBox(this, this.menu); - } - }; - - /** - * Perform an undo action - * @private - */ - treemode._onUndo = function () { - if (this.history) { - // undo last action - this.history.undo(); - - // fire change event - this._onChange(); - } - }; - - /** - * Perform a redo action - * @private - */ - treemode._onRedo = function () { - if (this.history) { - // redo last action - this.history.redo(); - - // fire change event - this._onChange(); - } - }; - - /** - * Event handler - * @param event - * @private - */ - treemode._onEvent = function (event) { - if (event.type == 'keydown') { - this._onKeyDown(event); - } - - if (event.type == 'focus') { - this.focusTarget = event.target; - } - - if (event.type == 'mousedown') { - this._startDragDistance(event); - } - if (event.type == 'mousemove' || event.type == 'mouseup' || event.type == 'click') { - this._updateDragDistance(event); - } - - var node = Node.getNodeFromTarget(event.target); - - if (node && node.selected) { - if (event.type == 'click') { - if (event.target == node.dom.menu) { - this.showContextMenu(event.target); - - // stop propagation (else we will open the context menu of a single node) - return; - } - - // deselect a multi selection - if (!event.hasMoved) { - this.deselect(); - } - } - - if (event.type == 'mousedown') { - // drag multiple nodes - Node.onDragStart(this.multiselection.nodes, event); - } - } - else { - if (event.type == 'mousedown') { - this.deselect(); - - if (node && event.target == node.dom.drag) { - // drag a singe node - Node.onDragStart(node, event); - } - else if (!node || (event.target != node.dom.field && event.target != node.dom.value && event.target != node.dom.select)) { - // select multiple nodes - this._onMultiSelectStart(event); - } - } - } - - if (node) { - node.onEvent(event); - } - }; - - treemode._startDragDistance = function (event) { - this.dragDistanceEvent = { - initialTarget: event.target, - initialPageX: event.pageX, - initialPageY: event.pageY, - dragDistance: 0, - hasMoved: false - }; - }; - - treemode._updateDragDistance = function (event) { - if (!this.dragDistanceEvent) { - this._startDragDistance(event); - } - - var diffX = event.pageX - this.dragDistanceEvent.initialPageX; - var diffY = event.pageY - this.dragDistanceEvent.initialPageY; - - this.dragDistanceEvent.dragDistance = Math.sqrt(diffX * diffX + diffY * diffY); - this.dragDistanceEvent.hasMoved = - this.dragDistanceEvent.hasMoved || this.dragDistanceEvent.dragDistance > 10; - - event.dragDistance = this.dragDistanceEvent.dragDistance; - event.hasMoved = this.dragDistanceEvent.hasMoved; - - return event.dragDistance; - }; - - /** - * Start multi selection of nodes by dragging the mouse - * @param event - * @private - */ - treemode._onMultiSelectStart = function (event) { - var node = Node.getNodeFromTarget(event.target); - - if (this.options.mode !== 'tree' || this.options.onEditable !== undefined) { - // dragging not allowed in modes 'view' and 'form' - // TODO: allow multiselection of items when option onEditable is specified - return; - } - - this.multiselection = { - start: node || null, - end: null, - nodes: [] - }; - - this._startDragDistance(event); - - var editor = this; - if (!this.mousemove) { - this.mousemove = util.addEventListener(window, 'mousemove', function (event) { - editor._onMultiSelect(event); - }); - } - if (!this.mouseup) { - this.mouseup = util.addEventListener(window, 'mouseup', function (event ) { - editor._onMultiSelectEnd(event); - }); - } - - }; - - /** - * Multiselect nodes by dragging - * @param event - * @private - */ - treemode._onMultiSelect = function (event) { - event.preventDefault(); - - this._updateDragDistance(event); - if (!event.hasMoved) { - return; - } - - var node = Node.getNodeFromTarget(event.target); - - if (node) { - if (this.multiselection.start == null) { - this.multiselection.start = node; - } - this.multiselection.end = node; - } - - // deselect previous selection - this.deselect(); - - // find the selected nodes in the range from first to last - var start = this.multiselection.start; - var end = this.multiselection.end || this.multiselection.start; - if (start && end) { - // find the top level childs, all having the same parent - this.multiselection.nodes = this._findTopLevelNodes(start, end); - this.select(this.multiselection.nodes); - } - }; - - /** - * End of multiselect nodes by dragging - * @param event - * @private - */ - treemode._onMultiSelectEnd = function (event) { - // set focus to the context menu button of the first node - if (this.multiselection.nodes[0]) { - this.multiselection.nodes[0].dom.menu.focus(); - } - - this.multiselection.start = null; - this.multiselection.end = null; - - // cleanup global event listeners - if (this.mousemove) { - util.removeEventListener(window, 'mousemove', this.mousemove); - delete this.mousemove; - } - if (this.mouseup) { - util.removeEventListener(window, 'mouseup', this.mouseup); - delete this.mouseup; - } - }; - - /** - * deselect currently selected nodes - * @param {boolean} [clearStartAndEnd=false] If true, the `start` and `end` - * state is cleared too. - */ - treemode.deselect = function (clearStartAndEnd) { - this.multiselection.nodes.forEach(function (node) { - node.setSelected(false); - }); - this.multiselection.nodes = []; - - if (clearStartAndEnd) { - this.multiselection.start = null; - this.multiselection.end = null; - } - }; - - /** - * select nodes - * @param {Node[] | Node} nodes - */ - treemode.select = function (nodes) { - if (!Array.isArray(nodes)) { - return this.select([nodes]); - } - - if (nodes) { - this.deselect(); - - this.multiselection.nodes = nodes.slice(0); - - var first = nodes[0]; - nodes.forEach(function (node) { - node.setSelected(true, node === first); - }); - } - }; - - /** - * From two arbitrary selected nodes, find their shared parent node. - * From that parent node, select the two child nodes in the brances going to - * nodes `start` and `end`, and select all childs in between. - * @param {Node} start - * @param {Node} end - * @return {Array.} Returns an ordered list with child nodes - * @private - */ - treemode._findTopLevelNodes = function (start, end) { - var startPath = start.getNodePath(); - var endPath = end.getNodePath(); - var i = 0; - while (i < startPath.length && startPath[i] === endPath[i]) { - i++; - } - var root = startPath[i - 1]; - var startChild = startPath[i]; - var endChild = endPath[i]; - - if (!startChild || !endChild) { - if (root.parent) { - // startChild is a parent of endChild or vice versa - startChild = root; - endChild = root; - root = root.parent - } - else { - // we have selected the root node (which doesn't have a parent) - startChild = root.childs[0]; - endChild = root.childs[root.childs.length - 1]; - } - } - - if (root && startChild && endChild) { - var startIndex = root.childs.indexOf(startChild); - var endIndex = root.childs.indexOf(endChild); - var firstIndex = Math.min(startIndex, endIndex); - var lastIndex = Math.max(startIndex, endIndex); - - return root.childs.slice(firstIndex, lastIndex + 1); - } - else { - return []; - } - }; - - /** - * Event handler for keydown. Handles shortcut keys - * @param {Event} event - * @private - */ - treemode._onKeyDown = function (event) { - var keynum = event.which || event.keyCode; - var ctrlKey = event.ctrlKey; - var shiftKey = event.shiftKey; - var handled = false; - - if (keynum == 9) { // Tab or Shift+Tab - var me = this; - setTimeout(function () { - // select all text when moving focus to an editable div - util.selectContentEditable(me.focusTarget); - }, 0); - } - - if (this.searchBox) { - if (ctrlKey && keynum == 70) { // Ctrl+F - this.searchBox.dom.search.focus(); - this.searchBox.dom.search.select(); - handled = true; - } - else if (keynum == 114 || (ctrlKey && keynum == 71)) { // F3 or Ctrl+G - var focus = true; - if (!shiftKey) { - // select next search result (F3 or Ctrl+G) - this.searchBox.next(focus); - } - else { - // select previous search result (Shift+F3 or Ctrl+Shift+G) - this.searchBox.previous(focus); - } - - handled = true; - } - } - - if (this.history) { - if (ctrlKey && !shiftKey && keynum == 90) { // Ctrl+Z - // undo - this._onUndo(); - handled = true; - } - else if (ctrlKey && shiftKey && keynum == 90) { // Ctrl+Shift+Z - // redo - this._onRedo(); - handled = true; - } - } - - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - }; - - /** - * Create main table - * @private - */ - treemode._createTable = function () { - var contentOuter = document.createElement('div'); - contentOuter.className = 'jsoneditor-outer'; - this.contentOuter = contentOuter; - - this.content = document.createElement('div'); - this.content.className = 'jsoneditor-tree'; - contentOuter.appendChild(this.content); - - this.table = document.createElement('table'); - this.table.className = 'jsoneditor-tree'; - this.content.appendChild(this.table); - - // create colgroup where the first two columns don't have a fixed - // width, and the edit columns do have a fixed width - var col; - this.colgroupContent = document.createElement('colgroup'); - if (this.options.mode === 'tree') { - col = document.createElement('col'); - col.width = "24px"; - this.colgroupContent.appendChild(col); - } - col = document.createElement('col'); - col.width = "24px"; - this.colgroupContent.appendChild(col); - col = document.createElement('col'); - this.colgroupContent.appendChild(col); - this.table.appendChild(this.colgroupContent); - - this.tbody = document.createElement('tbody'); - this.table.appendChild(this.tbody); - - this.frame.appendChild(contentOuter); - }; - - /** - * Show a contextmenu for this node. - * Used for multiselection - * @param {HTMLElement} anchor Anchor element to attache the context menu to. - * @param {function} [onClose] Callback method called when the context menu - * is being closed. - */ - treemode.showContextMenu = function (anchor, onClose) { - var items = []; - var editor = this; - - // create duplicate button - items.push({ - text: 'Duplicate', - title: 'Duplicate selected fields (Ctrl+D)', - className: 'jsoneditor-duplicate', - click: function () { - Node.onDuplicate(editor.multiselection.nodes); - } - }); - - // create remove button - items.push({ - text: 'Remove', - title: 'Remove selected fields (Ctrl+Del)', - className: 'jsoneditor-remove', - click: function () { - Node.onRemove(editor.multiselection.nodes); - } - }); - - var menu = new ContextMenu(items, {close: onClose}); - menu.show(anchor, this.content); - }; - - - // define modes - module.exports = [ - { - mode: 'tree', - mixin: treemode, - data: 'json' - }, - { - mode: 'view', - mixin: treemode, - data: 'json' - }, - { - mode: 'form', - mixin: treemode, - data: 'json' - } - ]; - - -/***/ }, -/* 2 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * The highlighter can highlight/unhighlight a node, and - * animate the visibility of a context menu. - * @constructor Highlighter - */ - function Highlighter () { - this.locked = false; - } - - /** - * Hightlight given node and its childs - * @param {Node} node - */ - Highlighter.prototype.highlight = function (node) { - if (this.locked) { - return; - } - - if (this.node != node) { - // unhighlight current node - if (this.node) { - this.node.setHighlight(false); - } - - // highlight new node - this.node = node; - this.node.setHighlight(true); - } - - // cancel any current timeout - this._cancelUnhighlight(); - }; - - /** - * Unhighlight currently highlighted node. - * Will be done after a delay - */ - Highlighter.prototype.unhighlight = function () { - if (this.locked) { - return; - } - - var me = this; - if (this.node) { - this._cancelUnhighlight(); - - // do the unhighlighting after a small delay, to prevent re-highlighting - // the same node when moving from the drag-icon to the contextmenu-icon - // or vice versa. - this.unhighlightTimer = setTimeout(function () { - me.node.setHighlight(false); - me.node = undefined; - me.unhighlightTimer = undefined; - }, 0); - } - }; - - /** - * Cancel an unhighlight action (if before the timeout of the unhighlight action) - * @private - */ - Highlighter.prototype._cancelUnhighlight = function () { - if (this.unhighlightTimer) { - clearTimeout(this.unhighlightTimer); - this.unhighlightTimer = undefined; - } - }; - - /** - * Lock highlighting or unhighlighting nodes. - * methods highlight and unhighlight do not work while locked. - */ - Highlighter.prototype.lock = function () { - this.locked = true; - }; - - /** - * Unlock highlighting or unhighlighting nodes - */ - Highlighter.prototype.unlock = function () { - this.locked = false; - }; - - module.exports = Highlighter; - - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var util = __webpack_require__(4); - - /** - * @constructor History - * Store action history, enables undo and redo - * @param {JSONEditor} editor - */ - function History (editor) { - this.editor = editor; - this.history = []; - this.index = -1; - - this.clear(); - - // map with all supported actions - this.actions = { - 'editField': { - 'undo': function (params) { - params.node.updateField(params.oldValue); - }, - 'redo': function (params) { - params.node.updateField(params.newValue); - } - }, - 'editValue': { - 'undo': function (params) { - params.node.updateValue(params.oldValue); - }, - 'redo': function (params) { - params.node.updateValue(params.newValue); - } - }, - 'changeType': { - 'undo': function (params) { - params.node.changeType(params.oldType); - }, - 'redo': function (params) { - params.node.changeType(params.newType); - } - }, - - 'appendNodes': { - 'undo': function (params) { - params.nodes.forEach(function (node) { - params.parent.removeChild(node); - }); - }, - 'redo': function (params) { - params.nodes.forEach(function (node) { - params.parent.appendChild(node); - }); - } - }, - 'insertBeforeNodes': { - 'undo': function (params) { - params.nodes.forEach(function (node) { - params.parent.removeChild(node); - }); - }, - 'redo': function (params) { - params.nodes.forEach(function (node) { - params.parent.insertBefore(node, params.beforeNode); - }); - } - }, - 'insertAfterNodes': { - 'undo': function (params) { - params.nodes.forEach(function (node) { - params.parent.removeChild(node); - }); - }, - 'redo': function (params) { - var afterNode = params.afterNode; - params.nodes.forEach(function (node) { - params.parent.insertAfter(params.node, afterNode); - afterNode = node; - }); - } - }, - 'removeNodes': { - 'undo': function (params) { - var parent = params.parent; - var beforeNode = parent.childs[params.index] || parent.append; - params.nodes.forEach(function (node) { - parent.insertBefore(node, beforeNode); - }); - }, - 'redo': function (params) { - params.nodes.forEach(function (node) { - params.parent.removeChild(node); - }); - } - }, - 'duplicateNodes': { - 'undo': function (params) { - params.nodes.forEach(function (node) { - params.parent.removeChild(node); - }); - }, - 'redo': function (params) { - var afterNode = params.afterNode; - params.nodes.forEach(function (node) { - params.parent.insertAfter(node, afterNode); - afterNode = node; - }); - } - }, - 'moveNodes': { - 'undo': function (params) { - params.nodes.forEach(function (node) { - params.oldBeforeNode.parent.moveBefore(node, params.oldBeforeNode); - }); - }, - 'redo': function (params) { - params.nodes.forEach(function (node) { - params.newBeforeNode.parent.moveBefore(node, params.newBeforeNode); - }); - } - }, - - 'sort': { - 'undo': function (params) { - var node = params.node; - node.hideChilds(); - node.sort = params.oldSort; - node.childs = params.oldChilds; - node.showChilds(); - }, - 'redo': function (params) { - var node = params.node; - node.hideChilds(); - node.sort = params.newSort; - node.childs = params.newChilds; - node.showChilds(); - } - } - - // TODO: restore the original caret position and selection with each undo - // TODO: implement history for actions "expand", "collapse", "scroll", "setDocument" - }; - } - - /** - * The method onChange is executed when the History is changed, and can - * be overloaded. - */ - History.prototype.onChange = function () {}; - - /** - * Add a new action to the history - * @param {String} action The executed action. Available actions: "editField", - * "editValue", "changeType", "appendNode", - * "removeNode", "duplicateNode", "moveNode" - * @param {Object} params Object containing parameters describing the change. - * The parameters in params depend on the action (for - * example for "editValue" the Node, old value, and new - * value are provided). params contains all information - * needed to undo or redo the action. - */ - History.prototype.add = function (action, params) { - this.index++; - this.history[this.index] = { - 'action': action, - 'params': params, - 'timestamp': new Date() - }; - - // remove redo actions which are invalid now - if (this.index < this.history.length - 1) { - this.history.splice(this.index + 1, this.history.length - this.index - 1); - } - - // fire onchange event - this.onChange(); - }; - - /** - * Clear history - */ - History.prototype.clear = function () { - this.history = []; - this.index = -1; - - // fire onchange event - this.onChange(); - }; - - /** - * Check if there is an action available for undo - * @return {Boolean} canUndo - */ - History.prototype.canUndo = function () { - return (this.index >= 0); - }; - - /** - * Check if there is an action available for redo - * @return {Boolean} canRedo - */ - History.prototype.canRedo = function () { - return (this.index < this.history.length - 1); - }; - - /** - * Undo the last action - */ - History.prototype.undo = function () { - if (this.canUndo()) { - var obj = this.history[this.index]; - if (obj) { - var action = this.actions[obj.action]; - if (action && action.undo) { - action.undo(obj.params); - if (obj.params.oldSelection) { - this.editor.setSelection(obj.params.oldSelection); - } - } - else { - console.error(new Error('unknown action "' + obj.action + '"')); - } - } - this.index--; - - // fire onchange event - this.onChange(); - } - }; - - /** - * Redo the last action - */ - History.prototype.redo = function () { - if (this.canRedo()) { - this.index++; - - var obj = this.history[this.index]; - if (obj) { - var action = this.actions[obj.action]; - if (action && action.redo) { - action.redo(obj.params); - if (obj.params.newSelection) { - this.editor.setSelection(obj.params.newSelection); - } - } - else { - console.error(new Error('unknown action "' + obj.action + '"')); - } - } - - // fire onchange event - this.onChange(); - } - }; - - /** - * Destroy history - */ - History.prototype.destroy = function () { - this.editor = null; - - this.history = []; - this.index = -1; - }; - - module.exports = History; - - -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var jsonlint = __webpack_require__(5); - - /** - * Parse JSON using the parser built-in in the browser. - * On exception, the jsonString is validated and a detailed error is thrown. - * @param {String} jsonString - * @return {JSON} json - */ - exports.parse = function parse(jsonString) { - try { - return JSON.parse(jsonString); - } - catch (err) { - // try to throw a more detailed error message using validate - exports.validate(jsonString); - - // rethrow the original error - throw err; - } - }; - - /** - * Sanitize a JSON-like string containing. For example changes JavaScript - * notation into JSON notation. - * This function for example changes a string like "{a: 2, 'b': {c: 'd'}" - * into '{"a": 2, "b": {"c": "d"}' - * @param {string} jsString - * @returns {string} json - */ - exports.sanitize = function (jsString) { - // escape all single and double quotes inside strings - var chars = []; - var i = 0; - - //If JSON starts with a function (characters/digits/"_-"), remove this function. - //This is useful for "stripping" JSONP objects to become JSON - //For example: /* some comment */ function_12321321 ( [{"a":"b"}] ); => [{"a":"b"}] - var match = jsString.match(/^\s*(\/\*(.|[\r\n])*?\*\/)?\s*[\da-zA-Z_$]+\s*\(([\s\S]*)\)\s*;?\s*$/); - if (match) { - jsString = match[3]; - } - - // helper functions to get the current/prev/next character - function curr () { return jsString.charAt(i); } - function next() { return jsString.charAt(i + 1); } - function prev() { return jsString.charAt(i - 1); } - - // get the last parsed non-whitespace character - function lastNonWhitespace () { - var p = chars.length - 1; - - while (p >= 0) { - var pp = chars[p]; - if (pp !== ' ' && pp !== '\n' && pp !== '\r' && pp !== '\t') { // non whitespace - return pp; - } - p--; - } - - return ''; - } - - // skip a block comment '/* ... */' - function skipBlockComment () { - i += 2; - while (i < jsString.length && (curr() !== '*' || next() !== '/')) { - i++; - } - i += 2; - } - - // skip a comment '// ...' - function skipComment () { - i += 2; - while (i < jsString.length && (curr() !== '\n')) { - i++; - } - } - - // parse single or double quoted string - function parseString(quote) { - chars.push('"'); - i++; - var c = curr(); - while (i < jsString.length && c !== quote) { - if (c === '"' && prev() !== '\\') { - // unescaped double quote, escape it - chars.push('\\'); - } - - // handle escape character - if (c === '\\') { - i++; - c = curr(); - - // remove the escape character when followed by a single quote ', not needed - if (c !== '\'') { - chars.push('\\'); - } - } - chars.push(c); - - i++; - c = curr(); - } - if (c === quote) { - chars.push('"'); - i++; - } - } - - // parse an unquoted key - function parseKey() { - var specialValues = ['null', 'true', 'false']; - var key = ''; - var c = curr(); - - var regexp = /[a-zA-Z_$\d]/; // letter, number, underscore, dollar character - while (regexp.test(c)) { - key += c; - i++; - c = curr(); - } - - if (specialValues.indexOf(key) === -1) { - chars.push('"' + key + '"'); - } - else { - chars.push(key); - } - } - - while(i < jsString.length) { - var c = curr(); - - if (c === '/' && next() === '*') { - skipBlockComment(); - } - else if (c === '/' && next() === '/') { - skipComment(); - } - else if (c === '\'' || c === '"') { - parseString(c); - } - else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) { - // an unquoted object key (like a in '{a:2}') - parseKey(); - } - else { - chars.push(c); - i++; - } - } - - return chars.join(''); - }; - - /** - * Escape unicode characters. - * For example input '\u2661' (length 1) will output '\\u2661' (length 5). - * @param {string} text - * @return {string} - */ - exports.escapeUnicodeChars = function (text) { - // see https://www.wikiwand.com/en/UTF-16 - // note: we leave surrogate pairs as two individual chars, - // as JSON doesn't interpret them as a single unicode char. - return text.replace(/[\u007F-\uFFFF]/g, function(c) { - return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4); - }) - }; - - /** - * Validate a string containing a JSON object - * This method uses JSONLint to validate the String. If JSONLint is not - * available, the built-in JSON parser of the browser is used. - * @param {String} jsonString String with an (invalid) JSON object - * @throws Error - */ - exports.validate = function validate(jsonString) { - if (typeof(jsonlint) != 'undefined') { - jsonlint.parse(jsonString); - } - else { - JSON.parse(jsonString); - } - }; - - /** - * Extend object a with the properties of object b - * @param {Object} a - * @param {Object} b - * @return {Object} a - */ - exports.extend = function extend(a, b) { - for (var prop in b) { - if (b.hasOwnProperty(prop)) { - a[prop] = b[prop]; - } - } - return a; - }; - - /** - * Remove all properties from object a - * @param {Object} a - * @return {Object} a - */ - exports.clear = function clear (a) { - for (var prop in a) { - if (a.hasOwnProperty(prop)) { - delete a[prop]; - } - } - return a; - }; - - /** - * Get the type of an object - * @param {*} object - * @return {String} type - */ - exports.type = function type (object) { - if (object === null) { - return 'null'; - } - if (object === undefined) { - return 'undefined'; - } - if ((object instanceof Number) || (typeof object === 'number')) { - return 'number'; - } - if ((object instanceof String) || (typeof object === 'string')) { - return 'string'; - } - if ((object instanceof Boolean) || (typeof object === 'boolean')) { - return 'boolean'; - } - if ((object instanceof RegExp) || (typeof object === 'regexp')) { - return 'regexp'; - } - if (exports.isArray(object)) { - return 'array'; - } - - return 'object'; - }; - - /** - * Test whether a text contains a url (matches when a string starts - * with 'http://*' or 'https://*' and has no whitespace characters) - * @param {String} text - */ - var isUrlRegex = /^https?:\/\/\S+$/; - exports.isUrl = function isUrl (text) { - return (typeof text == 'string' || text instanceof String) && - isUrlRegex.test(text); - }; - - /** - * Tes whether given object is an Array - * @param {*} obj - * @returns {boolean} returns true when obj is an array - */ - exports.isArray = function (obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; - }; - - /** - * Retrieve the absolute left value of a DOM element - * @param {Element} elem A dom element, for example a div - * @return {Number} left The absolute left position of this element - * in the browser page. - */ - exports.getAbsoluteLeft = function getAbsoluteLeft(elem) { - var rect = elem.getBoundingClientRect(); - return rect.left + window.pageXOffset || document.scrollLeft || 0; - }; - - /** - * Retrieve the absolute top value of a DOM element - * @param {Element} elem A dom element, for example a div - * @return {Number} top The absolute top position of this element - * in the browser page. - */ - exports.getAbsoluteTop = function getAbsoluteTop(elem) { - var rect = elem.getBoundingClientRect(); - return rect.top + window.pageYOffset || document.scrollTop || 0; - }; - - /** - * add a className to the given elements style - * @param {Element} elem - * @param {String} className - */ - exports.addClassName = function addClassName(elem, className) { - var classes = elem.className.split(' '); - if (classes.indexOf(className) == -1) { - classes.push(className); // add the class to the array - elem.className = classes.join(' '); - } - }; - - /** - * add a className to the given elements style - * @param {Element} elem - * @param {String} className - */ - exports.removeClassName = function removeClassName(elem, className) { - var classes = elem.className.split(' '); - var index = classes.indexOf(className); - if (index != -1) { - classes.splice(index, 1); // remove the class from the array - elem.className = classes.join(' '); - } - }; - - /** - * Strip the formatting from the contents of a div - * the formatting from the div itself is not stripped, only from its childs. - * @param {Element} divElement - */ - exports.stripFormatting = function stripFormatting(divElement) { - var childs = divElement.childNodes; - for (var i = 0, iMax = childs.length; i < iMax; i++) { - var child = childs[i]; - - // remove the style - if (child.style) { - // TODO: test if child.attributes does contain style - child.removeAttribute('style'); - } - - // remove all attributes - var attributes = child.attributes; - if (attributes) { - for (var j = attributes.length - 1; j >= 0; j--) { - var attribute = attributes[j]; - if (attribute.specified === true) { - child.removeAttribute(attribute.name); - } - } - } - - // recursively strip childs - exports.stripFormatting(child); - } - }; - - /** - * Set focus to the end of an editable div - * code from Nico Burns - * http://stackoverflow.com/users/140293/nico-burns - * http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity - * @param {Element} contentEditableElement A content editable div - */ - exports.setEndOfContentEditable = function setEndOfContentEditable(contentEditableElement) { - var range, selection; - if(document.createRange) { - range = document.createRange();//Create a range (a range is a like the selection but invisible) - range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range - range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start - selection = window.getSelection();//get the selection object (allows you to change selection) - selection.removeAllRanges();//remove any selections already made - selection.addRange(range);//make the range you have just created the visible selection - } - }; - - /** - * Select all text of a content editable div. - * http://stackoverflow.com/a/3806004/1262753 - * @param {Element} contentEditableElement A content editable div - */ - exports.selectContentEditable = function selectContentEditable(contentEditableElement) { - if (!contentEditableElement || contentEditableElement.nodeName != 'DIV') { - return; - } - - var sel, range; - if (window.getSelection && document.createRange) { - range = document.createRange(); - range.selectNodeContents(contentEditableElement); - sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - } - }; - - /** - * Get text selection - * http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore - * @return {Range | TextRange | null} range - */ - exports.getSelection = function getSelection() { - if (window.getSelection) { - var sel = window.getSelection(); - if (sel.getRangeAt && sel.rangeCount) { - return sel.getRangeAt(0); - } - } - return null; - }; - - /** - * Set text selection - * http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore - * @param {Range | TextRange | null} range - */ - exports.setSelection = function setSelection(range) { - if (range) { - if (window.getSelection) { - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - } - } - }; - - /** - * Get selected text range - * @return {Object} params object containing parameters: - * {Number} startOffset - * {Number} endOffset - * {Element} container HTML element holding the - * selected text element - * Returns null if no text selection is found - */ - exports.getSelectionOffset = function getSelectionOffset() { - var range = exports.getSelection(); - - if (range && 'startOffset' in range && 'endOffset' in range && - range.startContainer && (range.startContainer == range.endContainer)) { - return { - startOffset: range.startOffset, - endOffset: range.endOffset, - container: range.startContainer.parentNode - }; - } - - return null; - }; - - /** - * Set selected text range in given element - * @param {Object} params An object containing: - * {Element} container - * {Number} startOffset - * {Number} endOffset - */ - exports.setSelectionOffset = function setSelectionOffset(params) { - if (document.createRange && window.getSelection) { - var selection = window.getSelection(); - if(selection) { - var range = document.createRange(); - - if (!params.container.firstChild) { - params.container.appendChild(document.createTextNode('')); - } - - // TODO: do not suppose that the first child of the container is a textnode, - // but recursively find the textnodes - range.setStart(params.container.firstChild, params.startOffset); - range.setEnd(params.container.firstChild, params.endOffset); - - exports.setSelection(range); - } - } - }; - - /** - * Get the inner text of an HTML element (for example a div element) - * @param {Element} element - * @param {Object} [buffer] - * @return {String} innerText - */ - exports.getInnerText = function getInnerText(element, buffer) { - var first = (buffer == undefined); - if (first) { - buffer = { - 'text': '', - 'flush': function () { - var text = this.text; - this.text = ''; - return text; - }, - 'set': function (text) { - this.text = text; - } - }; - } - - // text node - if (element.nodeValue) { - return buffer.flush() + element.nodeValue; - } - - // divs or other HTML elements - if (element.hasChildNodes()) { - var childNodes = element.childNodes; - var innerText = ''; - - for (var i = 0, iMax = childNodes.length; i < iMax; i++) { - var child = childNodes[i]; - - if (child.nodeName == 'DIV' || child.nodeName == 'P') { - var prevChild = childNodes[i - 1]; - var prevName = prevChild ? prevChild.nodeName : undefined; - if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') { - innerText += '\n'; - buffer.flush(); - } - innerText += exports.getInnerText(child, buffer); - buffer.set('\n'); - } - else if (child.nodeName == 'BR') { - innerText += buffer.flush(); - buffer.set('\n'); - } - else { - innerText += exports.getInnerText(child, buffer); - } - } - - return innerText; - } - else { - if (element.nodeName == 'P' && exports.getInternetExplorerVersion() != -1) { - // On Internet Explorer, a

with hasChildNodes()==false is - // rendered with a new line. Note that a

with - // hasChildNodes()==true is rendered without a new line - // Other browsers always ensure there is a
inside the

, - // and if not, the

does not render a new line - return buffer.flush(); - } - } - - // br or unknown - return ''; - }; - - /** - * Returns the version of Internet Explorer or a -1 - * (indicating the use of another browser). - * Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx - * @return {Number} Internet Explorer version, or -1 in case of an other browser - */ - exports.getInternetExplorerVersion = function getInternetExplorerVersion() { - if (_ieVersion == -1) { - var rv = -1; // Return value assumes failure. - if (navigator.appName == 'Microsoft Internet Explorer') - { - var ua = navigator.userAgent; - var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); - if (re.exec(ua) != null) { - rv = parseFloat( RegExp.$1 ); - } - } - - _ieVersion = rv; - } - - return _ieVersion; - }; - - /** - * Test whether the current browser is Firefox - * @returns {boolean} isFirefox - */ - exports.isFirefox = function isFirefox () { - return (navigator.userAgent.indexOf("Firefox") != -1); - }; - - /** - * cached internet explorer version - * @type {Number} - * @private - */ - var _ieVersion = -1; - - /** - * Add and event listener. Works for all browsers - * @param {Element} element An html element - * @param {string} action The action, for example "click", - * without the prefix "on" - * @param {function} listener The callback function to be executed - * @param {boolean} [useCapture] false by default - * @return {function} the created event listener - */ - exports.addEventListener = function addEventListener(element, action, listener, useCapture) { - if (element.addEventListener) { - if (useCapture === undefined) - useCapture = false; - - if (action === "mousewheel" && exports.isFirefox()) { - action = "DOMMouseScroll"; // For Firefox - } - - element.addEventListener(action, listener, useCapture); - return listener; - } else if (element.attachEvent) { - // Old IE browsers - var f = function () { - return listener.call(element, window.event); - }; - element.attachEvent("on" + action, f); - return f; - } - }; - - /** - * Remove an event listener from an element - * @param {Element} element An html dom element - * @param {string} action The name of the event, for example "mousedown" - * @param {function} listener The listener function - * @param {boolean} [useCapture] false by default - */ - exports.removeEventListener = function removeEventListener(element, action, listener, useCapture) { - if (element.removeEventListener) { - if (useCapture === undefined) - useCapture = false; - - if (action === "mousewheel" && exports.isFirefox()) { - action = "DOMMouseScroll"; // For Firefox - } - - element.removeEventListener(action, listener, useCapture); - } else if (element.detachEvent) { - // Old IE browsers - element.detachEvent("on" + action, listener); - } - }; - - /** - * Parse a JSON path like '.items[3].name' into an array - * @param {string} jsonPath - * @return {Array} - */ - exports.parsePath = function parsePath(jsonPath) { - var prop, remainder; - - if (jsonPath.length === 0) { - return []; - } - - // find a match like '.prop' - var match = jsonPath.match(/^\.(\w+)/); - if (match) { - prop = match[1]; - remainder = jsonPath.substr(prop.length + 1); - } - else if (jsonPath[0] === '[') { - // find a match like - var end = jsonPath.indexOf(']'); - if (end === -1) { - throw new SyntaxError('Character ] expected in path'); - } - if (end === 1) { - throw new SyntaxError('Index expected after ['); - } - - var value = jsonPath.substring(1, end); - prop = value === '*' ? value : JSON.parse(value); // parse string and number - remainder = jsonPath.substr(end + 1); - } - else { - throw new SyntaxError('Failed to parse path'); - } - - return [prop].concat(parsePath(remainder)) - }; - - /** - * Improve the error message of a JSON schema error - * @param {Object} error - * @return {Object} The error - */ - exports.improveSchemaError = function (error) { - if (error.keyword === 'enum' && Array.isArray(error.schema)) { - var enums = error.schema; - if (enums) { - enums = enums.map(function (value) { - return JSON.stringify(value); - }); - - if (enums.length > 5) { - var more = ['(' + (enums.length - 5) + ' more...)']; - enums = enums.slice(0, 5); - enums.push(more); - } - error.message = 'should be equal to one of: ' + enums.join(', '); - } - } - - if (error.keyword === 'additionalProperties') { - error.message = 'should NOT have additional property: ' + error.params.additionalProperty; - } - - return error; - }; - - /** - * Test whether the child rect fits completely inside the parent rect. - * @param {ClientRect} parent - * @param {ClientRect} child - * @param {number} margin - */ - exports.insideRect = function (parent, child, margin) { - var _margin = margin !== undefined ? margin : 0; - return child.left - _margin >= parent.left - && child.right + _margin <= parent.right - && child.top - _margin >= parent.top - && child.bottom + _margin <= parent.bottom; - }; - - /** - * Returns a function, that, as long as it continues to be invoked, will not - * be triggered. The function will be called after it stops being called for - * N milliseconds. - * - * Source: https://davidwalsh.name/javascript-debounce-function - * - * @param {function} func - * @param {number} wait Number in milliseconds - * @param {boolean} [immediate=false] If `immediate` is passed, trigger the - * function on the leading edge, instead - * of the trailing. - * @return {function} Return the debounced function - */ - exports.debounce = function debounce(func, wait, immediate) { - var timeout; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; - }; - - /** - * Determines the difference between two texts. - * Can only detect one removed or inserted block of characters. - * @param {string} oldText - * @param {string} newText - * @return {{start: number, end: number}} Returns the start and end - * of the changed part in newText. - */ - exports.textDiff = function textDiff(oldText, newText) { - var len = newText.length; - var start = 0; - var oldEnd = oldText.length; - var newEnd = newText.length; - - while (newText.charAt(start) === oldText.charAt(start) - && start < len) { - start++; - } - - while (newText.charAt(newEnd - 1) === oldText.charAt(oldEnd - 1) - && newEnd > start && oldEnd > 0) { - newEnd--; - oldEnd--; - } - - return {start: start, end: newEnd}; - }; - - -/***/ }, -/* 5 */ -/***/ function(module, exports, __webpack_require__) { - - /* 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 (true) { - exports.parser = jsonlint; - exports.parse = jsonlint.parse.bind(jsonlint); - } - -/***/ }, -/* 6 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * @constructor SearchBox - * Create a search box in given HTML container - * @param {JSONEditor} editor The JSON Editor to attach to - * @param {Element} container HTML container element of where to - * create the search box - */ - function SearchBox (editor, container) { - var searchBox = this; - - this.editor = editor; - this.timeout = undefined; - this.delay = 200; // ms - this.lastText = undefined; - - this.dom = {}; - this.dom.container = container; - - var table = document.createElement('table'); - this.dom.table = table; - table.className = 'jsoneditor-search'; - container.appendChild(table); - var tbody = document.createElement('tbody'); - this.dom.tbody = tbody; - table.appendChild(tbody); - var tr = document.createElement('tr'); - tbody.appendChild(tr); - - var td = document.createElement('td'); - tr.appendChild(td); - var results = document.createElement('div'); - this.dom.results = results; - results.className = 'jsoneditor-results'; - td.appendChild(results); - - td = document.createElement('td'); - tr.appendChild(td); - var divInput = document.createElement('div'); - this.dom.input = divInput; - divInput.className = 'jsoneditor-frame'; - divInput.title = 'Search fields and values'; - td.appendChild(divInput); - - // table to contain the text input and search button - var tableInput = document.createElement('table'); - divInput.appendChild(tableInput); - var tbodySearch = document.createElement('tbody'); - tableInput.appendChild(tbodySearch); - tr = document.createElement('tr'); - tbodySearch.appendChild(tr); - - var refreshSearch = document.createElement('button'); - refreshSearch.className = 'jsoneditor-refresh'; - td = document.createElement('td'); - td.appendChild(refreshSearch); - tr.appendChild(td); - - var search = document.createElement('input'); - this.dom.search = search; - search.oninput = function (event) { - searchBox._onDelayedSearch(event); - }; - search.onchange = function (event) { // For IE 9 - searchBox._onSearch(); - }; - search.onkeydown = function (event) { - searchBox._onKeyDown(event); - }; - search.onkeyup = function (event) { - searchBox._onKeyUp(event); - }; - refreshSearch.onclick = function (event) { - search.select(); - }; - - // TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819 - td = document.createElement('td'); - td.appendChild(search); - tr.appendChild(td); - - var searchNext = document.createElement('button'); - searchNext.title = 'Next result (Enter)'; - searchNext.className = 'jsoneditor-next'; - searchNext.onclick = function () { - searchBox.next(); - }; - td = document.createElement('td'); - td.appendChild(searchNext); - tr.appendChild(td); - - var searchPrevious = document.createElement('button'); - searchPrevious.title = 'Previous result (Shift+Enter)'; - searchPrevious.className = 'jsoneditor-previous'; - searchPrevious.onclick = function () { - searchBox.previous(); - }; - td = document.createElement('td'); - td.appendChild(searchPrevious); - tr.appendChild(td); - } - - /** - * Go to the next search result - * @param {boolean} [focus] If true, focus will be set to the next result - * focus is false by default. - */ - SearchBox.prototype.next = function(focus) { - if (this.results != undefined) { - var index = (this.resultIndex != undefined) ? this.resultIndex + 1 : 0; - if (index > this.results.length - 1) { - index = 0; - } - this._setActiveResult(index, focus); - } - }; - - /** - * Go to the prevous search result - * @param {boolean} [focus] If true, focus will be set to the next result - * focus is false by default. - */ - SearchBox.prototype.previous = function(focus) { - if (this.results != undefined) { - var max = this.results.length - 1; - var index = (this.resultIndex != undefined) ? this.resultIndex - 1 : max; - if (index < 0) { - index = max; - } - this._setActiveResult(index, focus); - } - }; - - /** - * Set new value for the current active result - * @param {Number} index - * @param {boolean} [focus] If true, focus will be set to the next result. - * focus is false by default. - * @private - */ - SearchBox.prototype._setActiveResult = function(index, focus) { - // de-activate current active result - if (this.activeResult) { - var prevNode = this.activeResult.node; - var prevElem = this.activeResult.elem; - if (prevElem == 'field') { - delete prevNode.searchFieldActive; - } - else { - delete prevNode.searchValueActive; - } - prevNode.updateDom(); - } - - if (!this.results || !this.results[index]) { - // out of range, set to undefined - this.resultIndex = undefined; - this.activeResult = undefined; - return; - } - - this.resultIndex = index; - - // set new node active - var node = this.results[this.resultIndex].node; - var elem = this.results[this.resultIndex].elem; - if (elem == 'field') { - node.searchFieldActive = true; - } - else { - node.searchValueActive = true; - } - this.activeResult = this.results[this.resultIndex]; - node.updateDom(); - - // TODO: not so nice that the focus is only set after the animation is finished - node.scrollTo(function () { - if (focus) { - node.focus(elem); - } - }); - }; - - /** - * Cancel any running onDelayedSearch. - * @private - */ - SearchBox.prototype._clearDelay = function() { - if (this.timeout != undefined) { - clearTimeout(this.timeout); - delete this.timeout; - } - }; - - /** - * Start a timer to execute a search after a short delay. - * Used for reducing the number of searches while typing. - * @param {Event} event - * @private - */ - SearchBox.prototype._onDelayedSearch = function (event) { - // execute the search after a short delay (reduces the number of - // search actions while typing in the search text box) - this._clearDelay(); - var searchBox = this; - this.timeout = setTimeout(function (event) { - searchBox._onSearch(); - }, - this.delay); - }; - - /** - * Handle onSearch event - * @param {boolean} [forceSearch] If true, search will be executed again even - * when the search text is not changed. - * Default is false. - * @private - */ - SearchBox.prototype._onSearch = function (forceSearch) { - this._clearDelay(); - - var value = this.dom.search.value; - var text = (value.length > 0) ? value : undefined; - if (text != this.lastText || forceSearch) { - // only search again when changed - this.lastText = text; - this.results = this.editor.search(text); - this._setActiveResult(undefined); - - // display search results - if (text != undefined) { - var resultCount = this.results.length; - switch (resultCount) { - case 0: this.dom.results.innerHTML = 'no results'; break; - case 1: this.dom.results.innerHTML = '1 result'; break; - default: this.dom.results.innerHTML = resultCount + ' results'; break; - } - } - else { - this.dom.results.innerHTML = ''; - } - } - }; - - /** - * Handle onKeyDown event in the input box - * @param {Event} event - * @private - */ - SearchBox.prototype._onKeyDown = function (event) { - var keynum = event.which; - if (keynum == 27) { // ESC - this.dom.search.value = ''; // clear search - this._onSearch(); - event.preventDefault(); - event.stopPropagation(); - } - else if (keynum == 13) { // Enter - if (event.ctrlKey) { - // force to search again - this._onSearch(true); - } - else if (event.shiftKey) { - // move to the previous search result - this.previous(); - } - else { - // move to the next search result - this.next(); - } - event.preventDefault(); - event.stopPropagation(); - } - }; - - /** - * Handle onKeyUp event in the input box - * @param {Event} event - * @private - */ - SearchBox.prototype._onKeyUp = function (event) { - var keynum = event.keyCode; - if (keynum != 27 && keynum != 13) { // !show and !Enter - this._onDelayedSearch(event); // For IE 9 - } - }; - - /** - * Clear the search results - */ - SearchBox.prototype.clear = function () { - this.dom.search.value = ''; - this._onSearch(); - }; - - /** - * Destroy the search box - */ - SearchBox.prototype.destroy = function () { - this.editor = null; - this.dom.container.removeChild(this.dom.table); - this.dom = null; - - this.results = null; - this.activeResult = null; - - this._clearDelay(); - - }; - - module.exports = SearchBox; - - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var util = __webpack_require__(4); - - /** - * A context menu - * @param {Object[]} items Array containing the menu structure - * TODO: describe structure - * @param {Object} [options] Object with options. Available options: - * {function} close Callback called when the - * context menu is being closed. - * @constructor - */ - function ContextMenu (items, options) { - this.dom = {}; - - var me = this; - var dom = this.dom; - this.anchor = undefined; - this.items = items; - this.eventListeners = {}; - this.selection = undefined; // holds the selection before the menu was opened - this.onClose = options ? options.close : undefined; - - // create root element - var root = document.createElement('div'); - root.className = 'jsoneditor-contextmenu-root'; - dom.root = root; - - // create a container element - var menu = document.createElement('div'); - menu.className = 'jsoneditor-contextmenu'; - dom.menu = menu; - root.appendChild(menu); - - // create a list to hold the menu items - var list = document.createElement('ul'); - list.className = 'jsoneditor-menu'; - menu.appendChild(list); - dom.list = list; - dom.items = []; // list with all buttons - - // create a (non-visible) button to set the focus to the menu - var focusButton = document.createElement('button'); - dom.focusButton = focusButton; - var li = document.createElement('li'); - li.style.overflow = 'hidden'; - li.style.height = '0'; - li.appendChild(focusButton); - list.appendChild(li); - - function createMenuItems (list, domItems, items) { - items.forEach(function (item) { - if (item.type == 'separator') { - // create a separator - var separator = document.createElement('div'); - separator.className = 'jsoneditor-separator'; - li = document.createElement('li'); - li.appendChild(separator); - list.appendChild(li); - } - else { - var domItem = {}; - - // create a menu item - var li = document.createElement('li'); - list.appendChild(li); - - // create a button in the menu item - var button = document.createElement('button'); - button.className = item.className; - domItem.button = button; - if (item.title) { - button.title = item.title; - } - if (item.click) { - button.onclick = function (event) { - event.preventDefault(); - me.hide(); - item.click(); - }; - } - li.appendChild(button); - - // create the contents of the button - if (item.submenu) { - // add the icon to the button - var divIcon = document.createElement('div'); - divIcon.className = 'jsoneditor-icon'; - button.appendChild(divIcon); - button.appendChild(document.createTextNode(item.text)); - - var buttonSubmenu; - if (item.click) { - // submenu and a button with a click handler - button.className += ' jsoneditor-default'; - - var buttonExpand = document.createElement('button'); - domItem.buttonExpand = buttonExpand; - buttonExpand.className = 'jsoneditor-expand'; - buttonExpand.innerHTML = '

'; - li.appendChild(buttonExpand); - if (item.submenuTitle) { - buttonExpand.title = item.submenuTitle; - } - - buttonSubmenu = buttonExpand; - } - else { - // submenu and a button without a click handler - var divExpand = document.createElement('div'); - divExpand.className = 'jsoneditor-expand'; - button.appendChild(divExpand); - - buttonSubmenu = button; - } - - // attach a handler to expand/collapse the submenu - buttonSubmenu.onclick = function (event) { - event.preventDefault(); - me._onExpandItem(domItem); - buttonSubmenu.focus(); - }; - - // create the submenu - var domSubItems = []; - domItem.subItems = domSubItems; - var ul = document.createElement('ul'); - domItem.ul = ul; - ul.className = 'jsoneditor-menu'; - ul.style.height = '0'; - li.appendChild(ul); - createMenuItems(ul, domSubItems, item.submenu); - } - else { - // no submenu, just a button with clickhandler - button.innerHTML = '
' + item.text; - } - - domItems.push(domItem); - } - }); - } - createMenuItems(list, this.dom.items, items); - - // TODO: when the editor is small, show the submenu on the right instead of inline? - - // calculate the max height of the menu with one submenu expanded - this.maxHeight = 0; // height in pixels - items.forEach(function (item) { - var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24; - me.maxHeight = Math.max(me.maxHeight, height); - }); - } - - /** - * Get the currently visible buttons - * @return {Array.} buttons - * @private - */ - ContextMenu.prototype._getVisibleButtons = function () { - var buttons = []; - var me = this; - this.dom.items.forEach(function (item) { - buttons.push(item.button); - if (item.buttonExpand) { - buttons.push(item.buttonExpand); - } - if (item.subItems && item == me.expandedItem) { - item.subItems.forEach(function (subItem) { - buttons.push(subItem.button); - if (subItem.buttonExpand) { - buttons.push(subItem.buttonExpand); - } - // TODO: change to fully recursive method - }); - } - }); - - return buttons; - }; - - // currently displayed context menu, a singleton. We may only have one visible context menu - ContextMenu.visibleMenu = undefined; - - /** - * Attach the menu to an anchor - * @param {HTMLElement} anchor Anchor where the menu will be attached - * as sibling. - * @param {HTMLElement} [contentWindow] The DIV with with the (scrollable) contents - */ - ContextMenu.prototype.show = function (anchor, contentWindow) { - this.hide(); - - // determine whether to display the menu below or above the anchor - var showBelow = true; - if (contentWindow) { - var anchorRect = anchor.getBoundingClientRect(); - var contentRect = contentWindow.getBoundingClientRect(); - - if (anchorRect.bottom + this.maxHeight < contentRect.bottom) { - // fits below -> show below - } - else if (anchorRect.top - this.maxHeight > contentRect.top) { - // fits above -> show above - showBelow = false; - } - else { - // doesn't fit above nor below -> show below - } - } - - // position the menu - if (showBelow) { - // display the menu below the anchor - var anchorHeight = anchor.offsetHeight; - this.dom.menu.style.left = '0px'; - this.dom.menu.style.top = anchorHeight + 'px'; - this.dom.menu.style.bottom = ''; - } - else { - // display the menu above the anchor - this.dom.menu.style.left = '0px'; - this.dom.menu.style.top = ''; - this.dom.menu.style.bottom = '0px'; - } - - // attach the menu to the parent of the anchor - var parent = anchor.parentNode; - parent.insertBefore(this.dom.root, parent.firstChild); - - // create and attach event listeners - var me = this; - var list = this.dom.list; - this.eventListeners.mousedown = util.addEventListener(window, 'mousedown', function (event) { - // hide menu on click outside of the menu - var target = event.target; - if ((target != list) && !me._isChildOf(target, list)) { - me.hide(); - event.stopPropagation(); - event.preventDefault(); - } - }); - this.eventListeners.keydown = util.addEventListener(window, 'keydown', function (event) { - me._onKeyDown(event); - }); - - // move focus to the first button in the context menu - this.selection = util.getSelection(); - this.anchor = anchor; - setTimeout(function () { - me.dom.focusButton.focus(); - }, 0); - - if (ContextMenu.visibleMenu) { - ContextMenu.visibleMenu.hide(); - } - ContextMenu.visibleMenu = this; - }; - - /** - * Hide the context menu if visible - */ - ContextMenu.prototype.hide = function () { - // remove the menu from the DOM - if (this.dom.root.parentNode) { - this.dom.root.parentNode.removeChild(this.dom.root); - if (this.onClose) { - this.onClose(); - } - } - - // remove all event listeners - // all event listeners are supposed to be attached to document. - for (var name in this.eventListeners) { - if (this.eventListeners.hasOwnProperty(name)) { - var fn = this.eventListeners[name]; - if (fn) { - util.removeEventListener(window, name, fn); - } - delete this.eventListeners[name]; - } - } - - if (ContextMenu.visibleMenu == this) { - ContextMenu.visibleMenu = undefined; - } - }; - - /** - * Expand a submenu - * Any currently expanded submenu will be hided. - * @param {Object} domItem - * @private - */ - ContextMenu.prototype._onExpandItem = function (domItem) { - var me = this; - var alreadyVisible = (domItem == this.expandedItem); - - // hide the currently visible submenu - var expandedItem = this.expandedItem; - if (expandedItem) { - //var ul = expandedItem.ul; - expandedItem.ul.style.height = '0'; - expandedItem.ul.style.padding = ''; - setTimeout(function () { - if (me.expandedItem != expandedItem) { - expandedItem.ul.style.display = ''; - util.removeClassName(expandedItem.ul.parentNode, 'jsoneditor-selected'); - } - }, 300); // timeout duration must match the css transition duration - this.expandedItem = undefined; - } - - if (!alreadyVisible) { - var ul = domItem.ul; - ul.style.display = 'block'; - var height = ul.clientHeight; // force a reflow in Firefox - setTimeout(function () { - if (me.expandedItem == domItem) { - ul.style.height = (ul.childNodes.length * 24) + 'px'; - ul.style.padding = '5px 10px'; - } - }, 0); - util.addClassName(ul.parentNode, 'jsoneditor-selected'); - this.expandedItem = domItem; - } - }; - - /** - * Handle onkeydown event - * @param {Event} event - * @private - */ - ContextMenu.prototype._onKeyDown = function (event) { - var target = event.target; - var keynum = event.which; - var handled = false; - var buttons, targetIndex, prevButton, nextButton; - - if (keynum == 27) { // ESC - // hide the menu on ESC key - - // restore previous selection and focus - if (this.selection) { - util.setSelection(this.selection); - } - if (this.anchor) { - this.anchor.focus(); - } - - this.hide(); - - handled = true; - } - else if (keynum == 9) { // Tab - if (!event.shiftKey) { // Tab - buttons = this._getVisibleButtons(); - targetIndex = buttons.indexOf(target); - if (targetIndex == buttons.length - 1) { - // move to first button - buttons[0].focus(); - handled = true; - } - } - else { // Shift+Tab - buttons = this._getVisibleButtons(); - targetIndex = buttons.indexOf(target); - if (targetIndex == 0) { - // move to last button - buttons[buttons.length - 1].focus(); - handled = true; - } - } - } - else if (keynum == 37) { // Arrow Left - if (target.className == 'jsoneditor-expand') { - buttons = this._getVisibleButtons(); - targetIndex = buttons.indexOf(target); - prevButton = buttons[targetIndex - 1]; - if (prevButton) { - prevButton.focus(); - } - } - handled = true; - } - else if (keynum == 38) { // Arrow Up - buttons = this._getVisibleButtons(); - targetIndex = buttons.indexOf(target); - prevButton = buttons[targetIndex - 1]; - if (prevButton && prevButton.className == 'jsoneditor-expand') { - // skip expand button - prevButton = buttons[targetIndex - 2]; - } - if (!prevButton) { - // move to last button - prevButton = buttons[buttons.length - 1]; - } - if (prevButton) { - prevButton.focus(); - } - handled = true; - } - else if (keynum == 39) { // Arrow Right - buttons = this._getVisibleButtons(); - targetIndex = buttons.indexOf(target); - nextButton = buttons[targetIndex + 1]; - if (nextButton && nextButton.className == 'jsoneditor-expand') { - nextButton.focus(); - } - handled = true; - } - else if (keynum == 40) { // Arrow Down - buttons = this._getVisibleButtons(); - targetIndex = buttons.indexOf(target); - nextButton = buttons[targetIndex + 1]; - if (nextButton && nextButton.className == 'jsoneditor-expand') { - // skip expand button - nextButton = buttons[targetIndex + 2]; - } - if (!nextButton) { - // move to first button - nextButton = buttons[0]; - } - if (nextButton) { - nextButton.focus(); - handled = true; - } - handled = true; - } - // TODO: arrow left and right - - if (handled) { - event.stopPropagation(); - event.preventDefault(); - } - }; - - /** - * Test if an element is a child of a parent element. - * @param {Element} child - * @param {Element} parent - * @return {boolean} isChild - */ - ContextMenu.prototype._isChildOf = function (child, parent) { - var e = child.parentNode; - while (e) { - if (e == parent) { - return true; - } - e = e.parentNode; - } - - return false; - }; - - module.exports = ContextMenu; - - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var naturalSort = __webpack_require__(9); - var ContextMenu = __webpack_require__(7); - var appendNodeFactory = __webpack_require__(10); - var util = __webpack_require__(4); - - /** - * @constructor Node - * Create a new Node - * @param {./treemode} editor - * @param {Object} [params] Can contain parameters: - * {string} field - * {boolean} fieldEditable - * {*} value - * {String} type Can have values 'auto', 'array', - * 'object', or 'string'. - */ - function Node (editor, params) { - /** @type {./treemode} */ - this.editor = editor; - this.dom = {}; - this.expanded = false; - - if(params && (params instanceof Object)) { - this.setField(params.field, params.fieldEditable); - this.setValue(params.value, params.type); - } - else { - this.setField(''); - this.setValue(null); - } - - this._debouncedOnChangeValue = util.debounce(this._onChangeValue.bind(this), Node.prototype.DEBOUNCE_INTERVAL); - this._debouncedOnChangeField = util.debounce(this._onChangeField.bind(this), Node.prototype.DEBOUNCE_INTERVAL); - } - - // debounce interval for keyboard input in milliseconds - Node.prototype.DEBOUNCE_INTERVAL = 150; - - /** - * Determine whether the field and/or value of this node are editable - * @private - */ - Node.prototype._updateEditability = function () { - this.editable = { - field: true, - value: true - }; - - if (this.editor) { - this.editable.field = this.editor.options.mode === 'tree'; - this.editable.value = this.editor.options.mode !== 'view'; - - if ((this.editor.options.mode === 'tree' || this.editor.options.mode === 'form') && - (typeof this.editor.options.onEditable === 'function')) { - var editable = this.editor.options.onEditable({ - field: this.field, - value: this.value, - path: this.getPath() - }); - - if (typeof editable === 'boolean') { - this.editable.field = editable; - this.editable.value = editable; - } - else { - if (typeof editable.field === 'boolean') this.editable.field = editable.field; - if (typeof editable.value === 'boolean') this.editable.value = editable.value; - } - } - } - }; - - /** - * Get the path of this node - * @return {String[]} Array containing the path to this node - */ - Node.prototype.getPath = function () { - var node = this; - var path = []; - while (node) { - var field = !node.parent - ? undefined // do not add an (optional) field name of the root node - : (node.parent.type != 'array') - ? node.field - : node.index; - - if (field !== undefined) { - path.unshift(field); - } - node = node.parent; - } - return path; - }; - - /** - * Find a Node from a JSON path like '.items[3].name' - * @param {string} jsonPath - * @return {Node | null} Returns the Node when found, returns null if not found - */ - Node.prototype.findNode = function (jsonPath) { - var path = util.parsePath(jsonPath); - var node = this; - while (node && path.length > 0) { - var prop = path.shift(); - if (typeof prop === 'number') { - if (node.type !== 'array') { - throw new Error('Cannot get child node at index ' + prop + ': node is no array'); - } - node = node.childs[prop]; - } - else { // string - if (node.type !== 'object') { - throw new Error('Cannot get child node ' + prop + ': node is no object'); - } - node = node.childs.filter(function (child) { - return child.field === prop; - })[0]; - } - } - - return node; - }; - - /** - * Find all parents of this node. The parents are ordered from root node towards - * the original node. - * @return {Array.} - */ - Node.prototype.findParents = function () { - var parents = []; - var parent = this.parent; - while (parent) { - parents.unshift(parent); - parent = parent.parent; - } - return parents; - }; - - /** - * - * @param {{dataPath: string, keyword: string, message: string, params: Object, schemaPath: string} | null} error - * @param {Node} [child] When this is the error of a parent node, pointing - * to an invalid child node, the child node itself - * can be provided. If provided, clicking the error - * icon will set focus to the invalid child node. - */ - Node.prototype.setError = function (error, child) { - // ensure the dom exists - this.getDom(); - - this.error = error; - var tdError = this.dom.tdError; - if (error) { - if (!tdError) { - tdError = document.createElement('td'); - this.dom.tdError = tdError; - this.dom.tdValue.parentNode.appendChild(tdError); - } - - var popover = document.createElement('div'); - popover.className = 'jsoneditor-popover jsoneditor-right'; - popover.appendChild(document.createTextNode(error.message)); - - var button = document.createElement('button'); - button.className = 'jsoneditor-schema-error'; - button.appendChild(popover); - - // update the direction of the popover - button.onmouseover = button.onfocus = function updateDirection() { - var directions = ['right', 'above', 'below', 'left']; - for (var i = 0; i < directions.length; i++) { - var direction = directions[i]; - popover.className = 'jsoneditor-popover jsoneditor-' + direction; - - var contentRect = this.editor.content.getBoundingClientRect(); - var popoverRect = popover.getBoundingClientRect(); - var margin = 20; // account for a scroll bar - var fit = util.insideRect(contentRect, popoverRect, margin); - - if (fit) { - break; - } - } - }.bind(this); - - // when clicking the error icon, expand all nodes towards the invalid - // child node, and set focus to the child node - if (child) { - button.onclick = function showInvalidNode() { - child.findParents().forEach(function (parent) { - parent.expand(false); - }); - - child.scrollTo(function () { - child.focus(); - }); - }; - } - - // apply the error message to the node - while (tdError.firstChild) { - tdError.removeChild(tdError.firstChild); - } - tdError.appendChild(button); - } - else { - if (tdError) { - this.dom.tdError.parentNode.removeChild(this.dom.tdError); - delete this.dom.tdError; - } - } - }; - - /** - * Get the index of this node: the index in the list of childs where this - * node is part of - * @return {number} Returns the index, or -1 if this is the root node - */ - Node.prototype.getIndex = function () { - return this.parent ? this.parent.childs.indexOf(this) : -1; - }; - - /** - * Set parent node - * @param {Node} parent - */ - Node.prototype.setParent = function(parent) { - this.parent = parent; - }; - - /** - * Set field - * @param {String} field - * @param {boolean} [fieldEditable] - */ - Node.prototype.setField = function(field, fieldEditable) { - this.field = field; - this.previousField = field; - this.fieldEditable = (fieldEditable === true); - }; - - /** - * Get field - * @return {String} - */ - Node.prototype.getField = function() { - if (this.field === undefined) { - this._getDomField(); - } - - return this.field; - }; - - /** - * Set value. Value is a JSON structure or an element String, Boolean, etc. - * @param {*} value - * @param {String} [type] Specify the type of the value. Can be 'auto', - * 'array', 'object', or 'string' - */ - Node.prototype.setValue = function(value, type) { - var childValue, child; - - // first clear all current childs (if any) - var childs = this.childs; - if (childs) { - while (childs.length) { - this.removeChild(childs[0]); - } - } - - // TODO: remove the DOM of this Node - - this.type = this._getType(value); - - // check if type corresponds with the provided type - if (type && type != this.type) { - if (type == 'string' && this.type == 'auto') { - this.type = type; - } - else { - throw new Error('Type mismatch: ' + - 'cannot cast value of type "' + this.type + - ' to the specified type "' + type + '"'); - } - } - - if (this.type == 'array') { - // array - this.childs = []; - for (var i = 0, iMax = value.length; i < iMax; i++) { - childValue = value[i]; - if (childValue !== undefined && !(childValue instanceof Function)) { - // ignore undefined and functions - child = new Node(this.editor, { - value: childValue - }); - this.appendChild(child); - } - } - this.value = ''; - } - else if (this.type == 'object') { - // object - this.childs = []; - for (var childField in value) { - if (value.hasOwnProperty(childField)) { - childValue = value[childField]; - if (childValue !== undefined && !(childValue instanceof Function)) { - // ignore undefined and functions - child = new Node(this.editor, { - field: childField, - value: childValue - }); - this.appendChild(child); - } - } - } - this.value = ''; - - // sort object keys - if (this.editor.options.sortObjectKeys === true) { - this.sort('asc'); - } - } - else { - // value - this.childs = undefined; - this.value = value; - } - - this.previousValue = this.value; - }; - - /** - * Get value. Value is a JSON structure - * @return {*} value - */ - Node.prototype.getValue = function() { - //var childs, i, iMax; - - if (this.type == 'array') { - var arr = []; - this.childs.forEach (function (child) { - arr.push(child.getValue()); - }); - return arr; - } - else if (this.type == 'object') { - var obj = {}; - this.childs.forEach (function (child) { - obj[child.getField()] = child.getValue(); - }); - return obj; - } - else { - if (this.value === undefined) { - this._getDomValue(); - } - - return this.value; - } - }; - - /** - * Get the nesting level of this node - * @return {Number} level - */ - Node.prototype.getLevel = function() { - return (this.parent ? this.parent.getLevel() + 1 : 0); - }; - - /** - * Get path of the root node till the current node - * @return {Node[]} Returns an array with nodes - */ - Node.prototype.getNodePath = function() { - var path = this.parent ? this.parent.getNodePath() : []; - path.push(this); - return path; - }; - - /** - * Create a clone of a node - * The complete state of a clone is copied, including whether it is expanded or - * not. The DOM elements are not cloned. - * @return {Node} clone - */ - Node.prototype.clone = function() { - var clone = new Node(this.editor); - clone.type = this.type; - clone.field = this.field; - clone.fieldInnerText = this.fieldInnerText; - clone.fieldEditable = this.fieldEditable; - clone.value = this.value; - clone.valueInnerText = this.valueInnerText; - clone.expanded = this.expanded; - - if (this.childs) { - // an object or array - var cloneChilds = []; - this.childs.forEach(function (child) { - var childClone = child.clone(); - childClone.setParent(clone); - cloneChilds.push(childClone); - }); - clone.childs = cloneChilds; - } - else { - // a value - clone.childs = undefined; - } - - return clone; - }; - - /** - * Expand this node and optionally its childs. - * @param {boolean} [recurse] Optional recursion, true by default. When - * true, all childs will be expanded recursively - */ - Node.prototype.expand = function(recurse) { - if (!this.childs) { - return; - } - - // set this node expanded - this.expanded = true; - if (this.dom.expand) { - this.dom.expand.className = 'jsoneditor-expanded'; - } - - this.showChilds(); - - if (recurse !== false) { - this.childs.forEach(function (child) { - child.expand(recurse); - }); - } - }; - - /** - * Collapse this node and optionally its childs. - * @param {boolean} [recurse] Optional recursion, true by default. When - * true, all childs will be collapsed recursively - */ - Node.prototype.collapse = function(recurse) { - if (!this.childs) { - return; - } - - this.hideChilds(); - - // collapse childs in case of recurse - if (recurse !== false) { - this.childs.forEach(function (child) { - child.collapse(recurse); - }); - - } - - // make this node collapsed - if (this.dom.expand) { - this.dom.expand.className = 'jsoneditor-collapsed'; - } - this.expanded = false; - }; - - /** - * Recursively show all childs when they are expanded - */ - Node.prototype.showChilds = function() { - var childs = this.childs; - if (!childs) { - return; - } - if (!this.expanded) { - return; - } - - var tr = this.dom.tr; - var table = tr ? tr.parentNode : undefined; - if (table) { - // show row with append button - var append = this.getAppend(); - var nextTr = tr.nextSibling; - if (nextTr) { - table.insertBefore(append, nextTr); - } - else { - table.appendChild(append); - } - - // show childs - this.childs.forEach(function (child) { - table.insertBefore(child.getDom(), append); - child.showChilds(); - }); - } - }; - - /** - * Hide the node with all its childs - */ - Node.prototype.hide = function() { - var tr = this.dom.tr; - var table = tr ? tr.parentNode : undefined; - if (table) { - table.removeChild(tr); - } - this.hideChilds(); - }; - - - /** - * Recursively hide all childs - */ - Node.prototype.hideChilds = function() { - var childs = this.childs; - if (!childs) { - return; - } - if (!this.expanded) { - return; - } - - // hide append row - var append = this.getAppend(); - if (append.parentNode) { - append.parentNode.removeChild(append); - } - - // hide childs - this.childs.forEach(function (child) { - child.hide(); - }); - }; - - - /** - * Add a new child to the node. - * Only applicable when Node value is of type array or object - * @param {Node} node - */ - Node.prototype.appendChild = function(node) { - if (this._hasChilds()) { - // adjust the link to the parent - node.setParent(this); - node.fieldEditable = (this.type == 'object'); - if (this.type == 'array') { - node.index = this.childs.length; - } - this.childs.push(node); - - if (this.expanded) { - // insert into the DOM, before the appendRow - var newTr = node.getDom(); - var appendTr = this.getAppend(); - var table = appendTr ? appendTr.parentNode : undefined; - if (appendTr && table) { - table.insertBefore(newTr, appendTr); - } - - node.showChilds(); - } - - this.updateDom({'updateIndexes': true}); - node.updateDom({'recurse': true}); - } - }; - - - /** - * Move a node from its current parent to this node - * Only applicable when Node value is of type array or object - * @param {Node} node - * @param {Node} beforeNode - */ - Node.prototype.moveBefore = function(node, beforeNode) { - if (this._hasChilds()) { - // create a temporary row, to prevent the scroll position from jumping - // when removing the node - var tbody = (this.dom.tr) ? this.dom.tr.parentNode : undefined; - if (tbody) { - var trTemp = document.createElement('tr'); - trTemp.style.height = tbody.clientHeight + 'px'; - tbody.appendChild(trTemp); - } - - if (node.parent) { - node.parent.removeChild(node); - } - - if (beforeNode instanceof AppendNode) { - this.appendChild(node); - } - else { - this.insertBefore(node, beforeNode); - } - - if (tbody) { - tbody.removeChild(trTemp); - } - } - }; - - /** - * Move a node from its current parent to this node - * Only applicable when Node value is of type array or object. - * If index is out of range, the node will be appended to the end - * @param {Node} node - * @param {Number} index - */ - Node.prototype.moveTo = function (node, index) { - if (node.parent == this) { - // same parent - var currentIndex = this.childs.indexOf(node); - if (currentIndex < index) { - // compensate the index for removal of the node itself - index++; - } - } - - var beforeNode = this.childs[index] || this.append; - this.moveBefore(node, beforeNode); - }; - - /** - * Insert a new child before a given node - * Only applicable when Node value is of type array or object - * @param {Node} node - * @param {Node} beforeNode - */ - Node.prototype.insertBefore = function(node, beforeNode) { - if (this._hasChilds()) { - if (beforeNode == this.append) { - // append to the child nodes - - // adjust the link to the parent - node.setParent(this); - node.fieldEditable = (this.type == 'object'); - this.childs.push(node); - } - else { - // insert before a child node - var index = this.childs.indexOf(beforeNode); - if (index == -1) { - throw new Error('Node not found'); - } - - // adjust the link to the parent - node.setParent(this); - node.fieldEditable = (this.type == 'object'); - this.childs.splice(index, 0, node); - } - - if (this.expanded) { - // insert into the DOM - var newTr = node.getDom(); - var nextTr = beforeNode.getDom(); - var table = nextTr ? nextTr.parentNode : undefined; - if (nextTr && table) { - table.insertBefore(newTr, nextTr); - } - - node.showChilds(); - } - - this.updateDom({'updateIndexes': true}); - node.updateDom({'recurse': true}); - } - }; - - /** - * Insert a new child before a given node - * Only applicable when Node value is of type array or object - * @param {Node} node - * @param {Node} afterNode - */ - Node.prototype.insertAfter = function(node, afterNode) { - if (this._hasChilds()) { - var index = this.childs.indexOf(afterNode); - var beforeNode = this.childs[index + 1]; - if (beforeNode) { - this.insertBefore(node, beforeNode); - } - else { - this.appendChild(node); - } - } - }; - - /** - * Search in this node - * The node will be expanded when the text is found one of its childs, else - * it will be collapsed. Searches are case insensitive. - * @param {String} text - * @return {Node[]} results Array with nodes containing the search text - */ - Node.prototype.search = function(text) { - var results = []; - var index; - var search = text ? text.toLowerCase() : undefined; - - // delete old search data - delete this.searchField; - delete this.searchValue; - - // search in field - if (this.field != undefined) { - var field = String(this.field).toLowerCase(); - index = field.indexOf(search); - if (index != -1) { - this.searchField = true; - results.push({ - 'node': this, - 'elem': 'field' - }); - } - - // update dom - this._updateDomField(); - } - - // search in value - if (this._hasChilds()) { - // array, object - - // search the nodes childs - if (this.childs) { - var childResults = []; - this.childs.forEach(function (child) { - childResults = childResults.concat(child.search(text)); - }); - results = results.concat(childResults); - } - - // update dom - if (search != undefined) { - var recurse = false; - if (childResults.length == 0) { - this.collapse(recurse); - } - else { - this.expand(recurse); - } - } - } - else { - // string, auto - if (this.value != undefined ) { - var value = String(this.value).toLowerCase(); - index = value.indexOf(search); - if (index != -1) { - this.searchValue = true; - results.push({ - 'node': this, - 'elem': 'value' - }); - } - } - - // update dom - this._updateDomValue(); - } - - return results; - }; - - /** - * Move the scroll position such that this node is in the visible area. - * The node will not get the focus - * @param {function(boolean)} [callback] - */ - Node.prototype.scrollTo = function(callback) { - if (!this.dom.tr || !this.dom.tr.parentNode) { - // if the node is not visible, expand its parents - var parent = this.parent; - var recurse = false; - while (parent) { - parent.expand(recurse); - parent = parent.parent; - } - } - - if (this.dom.tr && this.dom.tr.parentNode) { - this.editor.scrollTo(this.dom.tr.offsetTop, callback); - } - }; - - - // stores the element name currently having the focus - Node.focusElement = undefined; - - /** - * Set focus to this node - * @param {String} [elementName] The field name of the element to get the - * focus available values: 'drag', 'menu', - * 'expand', 'field', 'value' (default) - */ - Node.prototype.focus = function(elementName) { - Node.focusElement = elementName; - - if (this.dom.tr && this.dom.tr.parentNode) { - var dom = this.dom; - - switch (elementName) { - case 'drag': - if (dom.drag) { - dom.drag.focus(); - } - else { - dom.menu.focus(); - } - break; - - case 'menu': - dom.menu.focus(); - break; - - case 'expand': - if (this._hasChilds()) { - dom.expand.focus(); - } - else if (dom.field && this.fieldEditable) { - dom.field.focus(); - util.selectContentEditable(dom.field); - } - else if (dom.value && !this._hasChilds()) { - dom.value.focus(); - util.selectContentEditable(dom.value); - } - else { - dom.menu.focus(); - } - break; - - case 'field': - if (dom.field && this.fieldEditable) { - dom.field.focus(); - util.selectContentEditable(dom.field); - } - else if (dom.value && !this._hasChilds()) { - dom.value.focus(); - util.selectContentEditable(dom.value); - } - else if (this._hasChilds()) { - dom.expand.focus(); - } - else { - dom.menu.focus(); - } - break; - - case 'value': - default: - if (dom.value && !this._hasChilds()) { - dom.value.focus(); - util.selectContentEditable(dom.value); - } - else if (dom.field && this.fieldEditable) { - dom.field.focus(); - util.selectContentEditable(dom.field); - } - else if (this._hasChilds()) { - dom.expand.focus(); - } - else { - dom.menu.focus(); - } - break; - } - } - }; - - /** - * Select all text in an editable div after a delay of 0 ms - * @param {Element} editableDiv - */ - Node.select = function(editableDiv) { - setTimeout(function () { - util.selectContentEditable(editableDiv); - }, 0); - }; - - /** - * Update the values from the DOM field and value of this node - */ - Node.prototype.blur = function() { - // retrieve the actual field and value from the DOM. - this._getDomValue(false); - this._getDomField(false); - }; - - /** - * Check if given node is a child. The method will check recursively to find - * this node. - * @param {Node} node - * @return {boolean} containsNode - */ - Node.prototype.containsNode = function(node) { - if (this == node) { - return true; - } - - var childs = this.childs; - if (childs) { - // TODO: use the js5 Array.some() here? - for (var i = 0, iMax = childs.length; i < iMax; i++) { - if (childs[i].containsNode(node)) { - return true; - } - } - } - - return false; - }; - - /** - * Move given node into this node - * @param {Node} node the childNode to be moved - * @param {Node} beforeNode node will be inserted before given - * node. If no beforeNode is given, - * the node is appended at the end - * @private - */ - Node.prototype._move = function(node, beforeNode) { - if (node == beforeNode) { - // nothing to do... - return; - } - - // check if this node is not a child of the node to be moved here - if (node.containsNode(this)) { - throw new Error('Cannot move a field into a child of itself'); - } - - // remove the original node - if (node.parent) { - node.parent.removeChild(node); - } - - // create a clone of the node - var clone = node.clone(); - node.clearDom(); - - // insert or append the node - if (beforeNode) { - this.insertBefore(clone, beforeNode); - } - else { - this.appendChild(clone); - } - - /* TODO: adjust the field name (to prevent equal field names) - if (this.type == 'object') { - } - */ - }; - - /** - * Remove a child from the node. - * Only applicable when Node value is of type array or object - * @param {Node} node The child node to be removed; - * @return {Node | undefined} node The removed node on success, - * else undefined - */ - Node.prototype.removeChild = function(node) { - if (this.childs) { - var index = this.childs.indexOf(node); - - if (index != -1) { - node.hide(); - - // delete old search results - delete node.searchField; - delete node.searchValue; - - var removedNode = this.childs.splice(index, 1)[0]; - removedNode.parent = null; - - this.updateDom({'updateIndexes': true}); - - return removedNode; - } - } - - return undefined; - }; - - /** - * Remove a child node node from this node - * This method is equal to Node.removeChild, except that _remove fire an - * onChange event. - * @param {Node} node - * @private - */ - Node.prototype._remove = function (node) { - this.removeChild(node); - }; - - /** - * Change the type of the value of this Node - * @param {String} newType - */ - Node.prototype.changeType = function (newType) { - var oldType = this.type; - - if (oldType == newType) { - // type is not changed - return; - } - - if ((newType == 'string' || newType == 'auto') && - (oldType == 'string' || oldType == 'auto')) { - // this is an easy change - this.type = newType; - } - else { - // change from array to object, or from string/auto to object/array - var table = this.dom.tr ? this.dom.tr.parentNode : undefined; - var lastTr; - if (this.expanded) { - lastTr = this.getAppend(); - } - else { - lastTr = this.getDom(); - } - var nextTr = (lastTr && lastTr.parentNode) ? lastTr.nextSibling : undefined; - - // hide current field and all its childs - this.hide(); - this.clearDom(); - - // adjust the field and the value - this.type = newType; - - // adjust childs - if (newType == 'object') { - if (!this.childs) { - this.childs = []; - } - - this.childs.forEach(function (child, index) { - child.clearDom(); - delete child.index; - child.fieldEditable = true; - if (child.field == undefined) { - child.field = ''; - } - }); - - if (oldType == 'string' || oldType == 'auto') { - this.expanded = true; - } - } - else if (newType == 'array') { - if (!this.childs) { - this.childs = []; - } - - this.childs.forEach(function (child, index) { - child.clearDom(); - child.fieldEditable = false; - child.index = index; - }); - - if (oldType == 'string' || oldType == 'auto') { - this.expanded = true; - } - } - else { - this.expanded = false; - } - - // create new DOM - if (table) { - if (nextTr) { - table.insertBefore(this.getDom(), nextTr); - } - else { - table.appendChild(this.getDom()); - } - } - this.showChilds(); - } - - if (newType == 'auto' || newType == 'string') { - // cast value to the correct type - if (newType == 'string') { - this.value = String(this.value); - } - else { - this.value = this._stringCast(String(this.value)); - } - - this.focus(); - } - - this.updateDom({'updateIndexes': true}); - }; - - /** - * Retrieve value from DOM - * @param {boolean} [silent] If true (default), no errors will be thrown in - * case of invalid data - * @private - */ - Node.prototype._getDomValue = function(silent) { - if (this.dom.value && this.type != 'array' && this.type != 'object') { - this.valueInnerText = util.getInnerText(this.dom.value); - } - - if (this.valueInnerText != undefined) { - try { - // retrieve the value - var value; - if (this.type == 'string') { - value = this._unescapeHTML(this.valueInnerText); - } - else { - var str = this._unescapeHTML(this.valueInnerText); - value = this._stringCast(str); - } - if (value !== this.value) { - this.value = value; - this._debouncedOnChangeValue(); - } - } - catch (err) { - this.value = undefined; - // TODO: sent an action with the new, invalid value? - if (silent !== true) { - throw err; - } - } - } - }; - - /** - * Handle a changed value - * @private - */ - Node.prototype._onChangeValue = function () { - // get current selection, then override the range such that we can select - // the added/removed text on undo/redo - var oldSelection = this.editor.getSelection(); - if (oldSelection.range) { - var undoDiff = util.textDiff(String(this.value), String(this.previousValue)); - oldSelection.range.startOffset = undoDiff.start; - oldSelection.range.endOffset = undoDiff.end; - } - var newSelection = this.editor.getSelection(); - if (newSelection.range) { - var redoDiff = util.textDiff(String(this.previousValue), String(this.value)); - newSelection.range.startOffset = redoDiff.start; - newSelection.range.endOffset = redoDiff.end; - } - - this.editor._onAction('editValue', { - node: this, - oldValue: this.previousValue, - newValue: this.value, - oldSelection: oldSelection, - newSelection: newSelection - }); - - this.previousValue = this.value; - }; - - /** - * Handle a changed field - * @private - */ - Node.prototype._onChangeField = function () { - // get current selection, then override the range such that we can select - // the added/removed text on undo/redo - var oldSelection = this.editor.getSelection(); - if (oldSelection.range) { - var undoDiff = util.textDiff(this.field, this.previousField); - oldSelection.range.startOffset = undoDiff.start; - oldSelection.range.endOffset = undoDiff.end; - } - var newSelection = this.editor.getSelection(); - if (newSelection.range) { - var redoDiff = util.textDiff(this.previousField, this.field); - newSelection.range.startOffset = redoDiff.start; - newSelection.range.endOffset = redoDiff.end; - } - - this.editor._onAction('editField', { - node: this, - oldValue: this.previousField, - newValue: this.field, - oldSelection: oldSelection, - newSelection: newSelection - }); - - this.previousField = this.field; - }; - - /** - * Update dom value: - * - the text color of the value, depending on the type of the value - * - the height of the field, depending on the width - * - background color in case it is empty - * @private - */ - Node.prototype._updateDomValue = function () { - var domValue = this.dom.value; - if (domValue) { - var classNames = ['jsoneditor-value']; - - - // set text color depending on value type - var value = this.value; - var type = (this.type == 'auto') ? util.type(value) : this.type; - var isUrl = type == 'string' && util.isUrl(value); - classNames.push('jsoneditor-' + type); - if (isUrl) { - classNames.push('jsoneditor-url'); - } - - // visual styling when empty - var isEmpty = (String(this.value) == '' && this.type != 'array' && this.type != 'object'); - if (isEmpty) { - classNames.push('jsoneditor-empty'); - } - - // highlight when there is a search result - if (this.searchValueActive) { - classNames.push('jsoneditor-highlight-active'); - } - if (this.searchValue) { - classNames.push('jsoneditor-highlight'); - } - - domValue.className = classNames.join(' '); - - // update title - if (type == 'array' || type == 'object') { - var count = this.childs ? this.childs.length : 0; - domValue.title = this.type + ' containing ' + count + ' items'; - } - else if (isUrl && this.editable.value) { - domValue.title = 'Ctrl+Click or Ctrl+Enter to open url in new window'; - } - else { - domValue.title = ''; - } - - // show checkbox when the value is a boolean - if (type === 'boolean' && this.editable.value) { - if (!this.dom.checkbox) { - this.dom.checkbox = document.createElement('input'); - this.dom.checkbox.type = 'checkbox'; - this.dom.tdCheckbox = document.createElement('td'); - this.dom.tdCheckbox.className = 'jsoneditor-tree'; - this.dom.tdCheckbox.appendChild(this.dom.checkbox); - - this.dom.tdValue.parentNode.insertBefore(this.dom.tdCheckbox, this.dom.tdValue); - } - - this.dom.checkbox.checked = this.value; - } - else { - // cleanup checkbox when displayed - if (this.dom.tdCheckbox) { - this.dom.tdCheckbox.parentNode.removeChild(this.dom.tdCheckbox); - delete this.dom.tdCheckbox; - delete this.dom.checkbox; - } - } - - if (this.enum && this.editable.value) { - // create select box when this node has an enum object - if (!this.dom.select) { - this.dom.select = document.createElement('select'); - this.id = this.field + "_" + new Date().getUTCMilliseconds(); - this.dom.select.id = this.id; - this.dom.select.name = this.dom.select.id; - - //Create the default empty option - this.dom.select.option = document.createElement('option'); - this.dom.select.option.value = ''; - this.dom.select.option.innerHTML = '--'; - this.dom.select.appendChild(this.dom.select.option); - - //Iterate all enum values and add them as options - for(var i = 0; i < this.enum.length; i++) { - this.dom.select.option = document.createElement('option'); - this.dom.select.option.value = this.enum[i]; - this.dom.select.option.innerHTML = this.enum[i]; - if(this.dom.select.option.value == this.value){ - this.dom.select.option.selected = true; - } - this.dom.select.appendChild(this.dom.select.option); - } - - this.dom.tdSelect = document.createElement('td'); - this.dom.tdSelect.className = 'jsoneditor-tree'; - this.dom.tdSelect.appendChild(this.dom.select); - this.dom.tdValue.parentNode.insertBefore(this.dom.tdSelect, this.dom.tdValue); - } - - // If the enum is inside a composite type display - // both the simple input and the dropdown field - if(this.schema && ( - !this.schema.hasOwnProperty("oneOf") && - !this.schema.hasOwnProperty("anyOf") && - !this.schema.hasOwnProperty("allOf")) - ) { - this.valueFieldHTML = this.dom.tdValue.innerHTML; - this.dom.tdValue.style.visibility = 'hidden'; - this.dom.tdValue.innerHTML = ''; - } else { - delete this.valueFieldHTML; - } - } - else { - // cleanup select box when displayed - if (this.dom.tdSelect) { - this.dom.tdSelect.parentNode.removeChild(this.dom.tdSelect); - delete this.dom.tdSelect; - delete this.dom.select; - this.dom.tdValue.innerHTML = this.valueFieldHTML; - this.dom.tdValue.style.visibility = ''; - delete this.valueFieldHTML; - } - } - - // strip formatting from the contents of the editable div - util.stripFormatting(domValue); - } - }; - - /** - * Update dom field: - * - the text color of the field, depending on the text - * - the height of the field, depending on the width - * - background color in case it is empty - * @private - */ - Node.prototype._updateDomField = function () { - var domField = this.dom.field; - if (domField) { - // make backgound color lightgray when empty - var isEmpty = (String(this.field) == '' && this.parent.type != 'array'); - if (isEmpty) { - util.addClassName(domField, 'jsoneditor-empty'); - } - else { - util.removeClassName(domField, 'jsoneditor-empty'); - } - - // highlight when there is a search result - if (this.searchFieldActive) { - util.addClassName(domField, 'jsoneditor-highlight-active'); - } - else { - util.removeClassName(domField, 'jsoneditor-highlight-active'); - } - if (this.searchField) { - util.addClassName(domField, 'jsoneditor-highlight'); - } - else { - util.removeClassName(domField, 'jsoneditor-highlight'); - } - - // strip formatting from the contents of the editable div - util.stripFormatting(domField); - } - }; - - /** - * Retrieve field from DOM - * @param {boolean} [silent] If true (default), no errors will be thrown in - * case of invalid data - * @private - */ - Node.prototype._getDomField = function(silent) { - if (this.dom.field && this.fieldEditable) { - this.fieldInnerText = util.getInnerText(this.dom.field); - } - - if (this.fieldInnerText != undefined) { - try { - var field = this._unescapeHTML(this.fieldInnerText); - - if (field !== this.field) { - this.field = field; - this._debouncedOnChangeField(); - } - } - catch (err) { - this.field = undefined; - // TODO: sent an action here, with the new, invalid value? - if (silent !== true) { - throw err; - } - } - } - }; - - /** - * Validate this node and all it's childs - * @return {Array.<{node: Node, error: {message: string}}>} Returns a list with duplicates - */ - Node.prototype.validate = function () { - var errors = []; - - // find duplicate keys - if (this.type === 'object') { - var keys = {}; - var duplicateKeys = []; - for (var i = 0; i < this.childs.length; i++) { - var child = this.childs[i]; - if (keys[child.field]) { - duplicateKeys.push(child.field); - } - keys[child.field] = true; - } - - if (duplicateKeys.length > 0) { - errors = this.childs - .filter(function (node) { - return duplicateKeys.indexOf(node.field) !== -1; - }) - .map(function (node) { - return { - node: node, - error: { - message: 'duplicate key "' + node.field + '"' - } - } - }); - } - } - - // recurse over the childs - if (this.childs) { - for (var i = 0; i < this.childs.length; i++) { - var e = this.childs[i].validate(); - if (e.length > 0) { - errors = errors.concat(e); - } - } - } - - return errors; - }; - - /** - * Clear the dom of the node - */ - Node.prototype.clearDom = function() { - // TODO: hide the node first? - //this.hide(); - // TODO: recursively clear dom? - - this.dom = {}; - }; - - /** - * Get the HTML DOM TR element of the node. - * The dom will be generated when not yet created - * @return {Element} tr HTML DOM TR Element - */ - Node.prototype.getDom = function() { - var dom = this.dom; - if (dom.tr) { - return dom.tr; - } - - this._updateEditability(); - - // create row - dom.tr = document.createElement('tr'); - dom.tr.node = this; - - if (this.editor.options.mode === 'tree') { // note: we take here the global setting - var tdDrag = document.createElement('td'); - if (this.editable.field) { - // create draggable area - if (this.parent) { - var domDrag = document.createElement('button'); - dom.drag = domDrag; - domDrag.className = 'jsoneditor-dragarea'; - domDrag.title = 'Drag to move this field (Alt+Shift+Arrows)'; - tdDrag.appendChild(domDrag); - } - } - dom.tr.appendChild(tdDrag); - - // create context menu - var tdMenu = document.createElement('td'); - var menu = document.createElement('button'); - dom.menu = menu; - menu.className = 'jsoneditor-contextmenu'; - menu.title = 'Click to open the actions menu (Ctrl+M)'; - tdMenu.appendChild(dom.menu); - dom.tr.appendChild(tdMenu); - } - - // create tree and field - var tdField = document.createElement('td'); - dom.tr.appendChild(tdField); - dom.tree = this._createDomTree(); - tdField.appendChild(dom.tree); - - this.updateDom({'updateIndexes': true}); - - return dom.tr; - }; - - /** - * DragStart event, fired on mousedown on the dragarea at the left side of a Node - * @param {Node[] | Node} nodes - * @param {Event} event - */ - Node.onDragStart = function (nodes, event) { - if (!Array.isArray(nodes)) { - return Node.onDragStart([nodes], event); - } - if (nodes.length === 0) { - return; - } - - var firstNode = nodes[0]; - var lastNode = nodes[nodes.length - 1]; - var draggedNode = Node.getNodeFromTarget(event.target); - var beforeNode = lastNode._nextSibling(); - var editor = firstNode.editor; - - // in case of multiple selected nodes, offsetY prevents the selection from - // jumping when you start dragging one of the lower down nodes in the selection - var offsetY = util.getAbsoluteTop(draggedNode.dom.tr) - util.getAbsoluteTop(firstNode.dom.tr); - - if (!editor.mousemove) { - editor.mousemove = util.addEventListener(window, 'mousemove', function (event) { - Node.onDrag(nodes, event); - }); - } - - if (!editor.mouseup) { - editor.mouseup = util.addEventListener(window, 'mouseup',function (event ) { - Node.onDragEnd(nodes, event); - }); - } - - editor.highlighter.lock(); - editor.drag = { - oldCursor: document.body.style.cursor, - oldSelection: editor.getSelection(), - oldBeforeNode: beforeNode, - mouseX: event.pageX, - offsetY: offsetY, - level: firstNode.getLevel() - }; - document.body.style.cursor = 'move'; - - event.preventDefault(); - }; - - /** - * Drag event, fired when moving the mouse while dragging a Node - * @param {Node[] | Node} nodes - * @param {Event} event - */ - Node.onDrag = function (nodes, event) { - if (!Array.isArray(nodes)) { - return Node.onDrag([nodes], event); - } - if (nodes.length === 0) { - return; - } - - // TODO: this method has grown too large. Split it in a number of methods - var editor = nodes[0].editor; - var mouseY = event.pageY - editor.drag.offsetY; - var mouseX = event.pageX; - var trThis, trPrev, trNext, trFirst, trLast, trRoot; - var nodePrev, nodeNext; - var topThis, topPrev, topFirst, heightThis, bottomNext, heightNext; - var moved = false; - - // TODO: add an ESC option, which resets to the original position - - // move up/down - var firstNode = nodes[0]; - trThis = firstNode.dom.tr; - topThis = util.getAbsoluteTop(trThis); - heightThis = trThis.offsetHeight; - if (mouseY < topThis) { - // move up - trPrev = trThis; - do { - trPrev = trPrev.previousSibling; - nodePrev = Node.getNodeFromTarget(trPrev); - topPrev = trPrev ? util.getAbsoluteTop(trPrev) : 0; - } - while (trPrev && mouseY < topPrev); - - if (nodePrev && !nodePrev.parent) { - nodePrev = undefined; - } - - if (!nodePrev) { - // move to the first node - trRoot = trThis.parentNode.firstChild; - trPrev = trRoot ? trRoot.nextSibling : undefined; - nodePrev = Node.getNodeFromTarget(trPrev); - if (nodePrev == firstNode) { - nodePrev = undefined; - } - } - - if (nodePrev) { - // check if mouseY is really inside the found node - trPrev = nodePrev.dom.tr; - topPrev = trPrev ? util.getAbsoluteTop(trPrev) : 0; - if (mouseY > topPrev + heightThis) { - nodePrev = undefined; - } - } - - if (nodePrev) { - nodes.forEach(function (node) { - nodePrev.parent.moveBefore(node, nodePrev); - }); - moved = true; - } - } - else { - // move down - var lastNode = nodes[nodes.length - 1]; - trLast = (lastNode.expanded && lastNode.append) ? lastNode.append.getDom() : lastNode.dom.tr; - trFirst = trLast ? trLast.nextSibling : undefined; - if (trFirst) { - topFirst = util.getAbsoluteTop(trFirst); - trNext = trFirst; - do { - nodeNext = Node.getNodeFromTarget(trNext); - if (trNext) { - bottomNext = trNext.nextSibling ? - util.getAbsoluteTop(trNext.nextSibling) : 0; - heightNext = trNext ? (bottomNext - topFirst) : 0; - - if (nodeNext.parent.childs.length == nodes.length && - nodeNext.parent.childs[nodes.length - 1] == lastNode) { - // We are about to remove the last child of this parent, - // which will make the parents appendNode visible. - topThis += 27; - // TODO: dangerous to suppose the height of the appendNode a constant of 27 px. - } - } - - trNext = trNext.nextSibling; - } - while (trNext && mouseY > topThis + heightNext); - - if (nodeNext && nodeNext.parent) { - // calculate the desired level - var diffX = (mouseX - editor.drag.mouseX); - var diffLevel = Math.round(diffX / 24 / 2); - var level = editor.drag.level + diffLevel; // desired level - var levelNext = nodeNext.getLevel(); // level to be - - // find the best fitting level (move upwards over the append nodes) - trPrev = nodeNext.dom.tr.previousSibling; - while (levelNext < level && trPrev) { - nodePrev = Node.getNodeFromTarget(trPrev); - - var isDraggedNode = nodes.some(function (node) { - return node === nodePrev || nodePrev._isChildOf(node); - }); - - if (isDraggedNode) { - // neglect the dragged nodes themselves and their childs - } - else if (nodePrev instanceof AppendNode) { - var childs = nodePrev.parent.childs; - if (childs.length != nodes.length || childs[nodes.length - 1] != lastNode) { - // non-visible append node of a list of childs - // consisting of not only this node (else the - // append node will change into a visible "empty" - // text when removing this node). - nodeNext = Node.getNodeFromTarget(trPrev); - levelNext = nodeNext.getLevel(); - } - else { - break; - } - } - else { - break; - } - - trPrev = trPrev.previousSibling; - } - - // move the node when its position is changed - if (trLast.nextSibling != nodeNext.dom.tr) { - nodes.forEach(function (node) { - nodeNext.parent.moveBefore(node, nodeNext); - }); - moved = true; - } - } - } - } - - if (moved) { - // update the dragging parameters when moved - editor.drag.mouseX = mouseX; - editor.drag.level = firstNode.getLevel(); - } - - // auto scroll when hovering around the top of the editor - editor.startAutoScroll(mouseY); - - event.preventDefault(); - }; - - /** - * Drag event, fired on mouseup after having dragged a node - * @param {Node[] | Node} nodes - * @param {Event} event - */ - Node.onDragEnd = function (nodes, event) { - if (!Array.isArray(nodes)) { - return Node.onDrag([nodes], event); - } - if (nodes.length === 0) { - return; - } - - var firstNode = nodes[0]; - var editor = firstNode.editor; - var parent = firstNode.parent; - var firstIndex = parent.childs.indexOf(firstNode); - var beforeNode = parent.childs[firstIndex + nodes.length] || parent.append; - - // set focus to the context menu button of the first node - if (nodes[0]) { - nodes[0].dom.menu.focus(); - } - - var params = { - nodes: nodes, - oldSelection: editor.drag.oldSelection, - newSelection: editor.getSelection(), - oldBeforeNode: editor.drag.oldBeforeNode, - newBeforeNode: beforeNode - }; - - if (params.oldBeforeNode != params.newBeforeNode) { - // only register this action if the node is actually moved to another place - editor._onAction('moveNodes', params); - } - - document.body.style.cursor = editor.drag.oldCursor; - editor.highlighter.unlock(); - nodes.forEach(function (node) { - if (event.target !== node.dom.drag && event.target !== node.dom.menu) { - editor.highlighter.unhighlight(); - } - }); - delete editor.drag; - - if (editor.mousemove) { - util.removeEventListener(window, 'mousemove', editor.mousemove); - delete editor.mousemove; - } - if (editor.mouseup) { - util.removeEventListener(window, 'mouseup', editor.mouseup); - delete editor.mouseup; - } - - // Stop any running auto scroll - editor.stopAutoScroll(); - - event.preventDefault(); - }; - - /** - * Test if this node is a child of an other node - * @param {Node} node - * @return {boolean} isChild - * @private - */ - Node.prototype._isChildOf = function (node) { - var n = this.parent; - while (n) { - if (n == node) { - return true; - } - n = n.parent; - } - - return false; - }; - - /** - * Create an editable field - * @return {Element} domField - * @private - */ - Node.prototype._createDomField = function () { - return document.createElement('div'); - }; - - /** - * Set highlighting for this node and all its childs. - * Only applied to the currently visible (expanded childs) - * @param {boolean} highlight - */ - Node.prototype.setHighlight = function (highlight) { - if (this.dom.tr) { - if (highlight) { - util.addClassName(this.dom.tr, 'jsoneditor-highlight'); - } - else { - util.removeClassName(this.dom.tr, 'jsoneditor-highlight'); - } - - if (this.append) { - this.append.setHighlight(highlight); - } - - if (this.childs) { - this.childs.forEach(function (child) { - child.setHighlight(highlight); - }); - } - } - }; - - /** - * Select or deselect a node - * @param {boolean} selected - * @param {boolean} [isFirst] - */ - Node.prototype.setSelected = function (selected, isFirst) { - this.selected = selected; - - if (this.dom.tr) { - if (selected) { - util.addClassName(this.dom.tr, 'jsoneditor-selected'); - } - else { - util.removeClassName(this.dom.tr, 'jsoneditor-selected'); - } - - if (isFirst) { - util.addClassName(this.dom.tr, 'jsoneditor-first'); - } - else { - util.removeClassName(this.dom.tr, 'jsoneditor-first'); - } - - if (this.append) { - this.append.setSelected(selected); - } - - if (this.childs) { - this.childs.forEach(function (child) { - child.setSelected(selected); - }); - } - } - }; - - /** - * Update the value of the node. Only primitive types are allowed, no Object - * or Array is allowed. - * @param {String | Number | Boolean | null} value - */ - Node.prototype.updateValue = function (value) { - this.value = value; - this.updateDom(); - }; - - /** - * Update the field of the node. - * @param {String} field - */ - Node.prototype.updateField = function (field) { - this.field = field; - this.updateDom(); - }; - - /** - * Update the HTML DOM, optionally recursing through the childs - * @param {Object} [options] Available parameters: - * {boolean} [recurse] If true, the - * DOM of the childs will be updated recursively. - * False by default. - * {boolean} [updateIndexes] If true, the childs - * indexes of the node will be updated too. False by - * default. - */ - Node.prototype.updateDom = function (options) { - // update level indentation - var domTree = this.dom.tree; - if (domTree) { - domTree.style.marginLeft = this.getLevel() * 24 + 'px'; - } - - // apply field to DOM - var domField = this.dom.field; - if (domField) { - if (this.fieldEditable) { - // parent is an object - domField.contentEditable = this.editable.field; - domField.spellcheck = false; - domField.className = 'jsoneditor-field'; - } - else { - // parent is an array this is the root node - domField.className = 'jsoneditor-readonly'; - } - - var fieldText; - if (this.index != undefined) { - fieldText = this.index; - } - else if (this.field != undefined) { - fieldText = this.field; - } - else if (this._hasChilds()) { - fieldText = this.type; - } - else { - fieldText = ''; - } - domField.innerHTML = this._escapeHTML(fieldText); - - this._updateSchema(); - } - - // apply value to DOM - var domValue = this.dom.value; - if (domValue) { - var count = this.childs ? this.childs.length : 0; - if (this.type == 'array') { - domValue.innerHTML = '[' + count + ']'; - util.addClassName(this.dom.tr, 'jsoneditor-expandable'); - } - else if (this.type == 'object') { - domValue.innerHTML = '{' + count + '}'; - util.addClassName(this.dom.tr, 'jsoneditor-expandable'); - } - else { - domValue.innerHTML = this._escapeHTML(this.value); - util.removeClassName(this.dom.tr, 'jsoneditor-expandable'); - } - } - - // update field and value - this._updateDomField(); - this._updateDomValue(); - - // update childs indexes - if (options && options.updateIndexes === true) { - // updateIndexes is true or undefined - this._updateDomIndexes(); - } - - if (options && options.recurse === true) { - // recurse is true or undefined. update childs recursively - if (this.childs) { - this.childs.forEach(function (child) { - child.updateDom(options); - }); - } - } - - // update row with append button - if (this.append) { - this.append.updateDom(); - } - }; - - /** - * Locate the JSON schema of the node and check for any enum type - * @private - */ - Node.prototype._updateSchema = function () { - //Locating the schema of the node and checking for any enum type - if(this.editor && this.editor.options) { - // find the part of the json schema matching this nodes path - this.schema = Node._findSchema(this.editor.options.schema, this.getPath()); - if (this.schema) { - this.enum = Node._findEnum(this.schema); - } - else { - delete this.enum; - } - } - }; - - /** - * find an enum definition in a JSON schema, as property `enum` or inside - * one of the schemas composites (`oneOf`, `anyOf`, `allOf`) - * @param {Object} schema - * @return {Array | null} Returns the enum when found, null otherwise. - * @private - */ - Node._findEnum = function (schema) { - if (schema.enum) { - return schema.enum; - } - - var composite = schema.oneOf || schema.anyOf || schema.allOf; - if (composite) { - var match = composite.filter(function (entry) {return entry.enum}); - if (match.length > 0) { - return match[0].enum; - } - } - - return null - }; - - /** - * Return the part of a JSON schema matching given path. - * @param {Object} schema - * @param {Array.} path - * @return {Object | null} - * @private - */ - Node._findSchema = function (schema, path) { - var childSchema = schema; - - for (var i = 0; i < path.length && childSchema; i++) { - var key = path[i]; - if (typeof key === 'string' && childSchema.properties) { - childSchema = childSchema.properties[key] || null - } - else if (typeof key === 'number' && childSchema.items) { - childSchema = childSchema.items - } - } - - return childSchema - }; - - /** - * Update the DOM of the childs of a node: update indexes and undefined field - * names. - * Only applicable when structure is an array or object - * @private - */ - Node.prototype._updateDomIndexes = function () { - var domValue = this.dom.value; - var childs = this.childs; - if (domValue && childs) { - if (this.type == 'array') { - childs.forEach(function (child, index) { - child.index = index; - var childField = child.dom.field; - if (childField) { - childField.innerHTML = index; - } - }); - } - else if (this.type == 'object') { - childs.forEach(function (child) { - if (child.index != undefined) { - delete child.index; - - if (child.field == undefined) { - child.field = ''; - } - } - }); - } - } - }; - - /** - * Create an editable value - * @private - */ - Node.prototype._createDomValue = function () { - var domValue; - - if (this.type == 'array') { - domValue = document.createElement('div'); - domValue.innerHTML = '[...]'; - } - else if (this.type == 'object') { - domValue = document.createElement('div'); - domValue.innerHTML = '{...}'; - } - else { - if (!this.editable.value && util.isUrl(this.value)) { - // create a link in case of read-only editor and value containing an url - domValue = document.createElement('a'); - domValue.href = this.value; - domValue.target = '_blank'; - domValue.innerHTML = this._escapeHTML(this.value); - } - else { - // create an editable or read-only div - domValue = document.createElement('div'); - domValue.contentEditable = this.editable.value; - domValue.spellcheck = false; - domValue.innerHTML = this._escapeHTML(this.value); - } - } - - return domValue; - }; - - /** - * Create an expand/collapse button - * @return {Element} expand - * @private - */ - Node.prototype._createDomExpandButton = function () { - // create expand button - var expand = document.createElement('button'); - if (this._hasChilds()) { - expand.className = this.expanded ? 'jsoneditor-expanded' : 'jsoneditor-collapsed'; - expand.title = - 'Click to expand/collapse this field (Ctrl+E). \n' + - 'Ctrl+Click to expand/collapse including all childs.'; - } - else { - expand.className = 'jsoneditor-invisible'; - expand.title = ''; - } - - return expand; - }; - - - /** - * Create a DOM tree element, containing the expand/collapse button - * @return {Element} domTree - * @private - */ - Node.prototype._createDomTree = function () { - var dom = this.dom; - var domTree = document.createElement('table'); - var tbody = document.createElement('tbody'); - domTree.style.borderCollapse = 'collapse'; // TODO: put in css - domTree.className = 'jsoneditor-values'; - domTree.appendChild(tbody); - var tr = document.createElement('tr'); - tbody.appendChild(tr); - - // create expand button - var tdExpand = document.createElement('td'); - tdExpand.className = 'jsoneditor-tree'; - tr.appendChild(tdExpand); - dom.expand = this._createDomExpandButton(); - tdExpand.appendChild(dom.expand); - dom.tdExpand = tdExpand; - - // create the field - var tdField = document.createElement('td'); - tdField.className = 'jsoneditor-tree'; - tr.appendChild(tdField); - dom.field = this._createDomField(); - tdField.appendChild(dom.field); - dom.tdField = tdField; - - // create a separator - var tdSeparator = document.createElement('td'); - tdSeparator.className = 'jsoneditor-tree'; - tr.appendChild(tdSeparator); - if (this.type != 'object' && this.type != 'array') { - tdSeparator.appendChild(document.createTextNode(':')); - tdSeparator.className = 'jsoneditor-separator'; - } - dom.tdSeparator = tdSeparator; - - // create the value - var tdValue = document.createElement('td'); - tdValue.className = 'jsoneditor-tree'; - tr.appendChild(tdValue); - dom.value = this._createDomValue(); - tdValue.appendChild(dom.value); - dom.tdValue = tdValue; - - return domTree; - }; - - /** - * Handle an event. The event is caught centrally by the editor - * @param {Event} event - */ - Node.prototype.onEvent = function (event) { - var type = event.type, - target = event.target || event.srcElement, - dom = this.dom, - node = this, - expandable = this._hasChilds(); - - // check if mouse is on menu or on dragarea. - // If so, highlight current row and its childs - if (target == dom.drag || target == dom.menu) { - if (type == 'mouseover') { - this.editor.highlighter.highlight(this); - } - else if (type == 'mouseout') { - this.editor.highlighter.unhighlight(); - } - } - - // context menu events - if (type == 'click' && target == dom.menu) { - var highlighter = node.editor.highlighter; - highlighter.highlight(node); - highlighter.lock(); - util.addClassName(dom.menu, 'jsoneditor-selected'); - this.showContextMenu(dom.menu, function () { - util.removeClassName(dom.menu, 'jsoneditor-selected'); - highlighter.unlock(); - highlighter.unhighlight(); - }); - } - - // expand events - if (type == 'click') { - if (target == dom.expand || - ((node.editor.options.mode === 'view' || node.editor.options.mode === 'form') && target.nodeName === 'DIV')) { - if (expandable) { - var recurse = event.ctrlKey; // with ctrl-key, expand/collapse all - this._onExpand(recurse); - } - } - } - - // swap the value of a boolean when the checkbox displayed left is clicked - if (type == 'change' && target == dom.checkbox) { - this.dom.value.innerHTML = !this.value; - this._getDomValue(); - } - - // update the value of the node based on the selected option - if (type == 'change' && target == dom.select) { - this.dom.value.innerHTML = dom.select.value; - this._getDomValue(); - this._updateDomValue(); - } - - // value events - var domValue = dom.value; - if (target == domValue) { - //noinspection FallthroughInSwitchStatementJS - switch (type) { - case 'blur': - case 'change': - this._getDomValue(true); - this._updateDomValue(); - if (this.value) { - domValue.innerHTML = this._escapeHTML(this.value); - } - break; - - case 'input': - //this._debouncedGetDomValue(true); // TODO - this._getDomValue(true); - this._updateDomValue(); - break; - - case 'keydown': - case 'mousedown': - // TODO: cleanup - this.editor.selection = this.editor.getSelection(); - break; - - case 'click': - if (event.ctrlKey || !this.editable.value) { - if (util.isUrl(this.value)) { - window.open(this.value, '_blank'); - } - } - break; - - case 'keyup': - //this._debouncedGetDomValue(true); // TODO - this._getDomValue(true); - this._updateDomValue(); - break; - - case 'cut': - case 'paste': - setTimeout(function () { - node._getDomValue(true); - node._updateDomValue(); - }, 1); - break; - } - } - - // field events - var domField = dom.field; - if (target == domField) { - switch (type) { - case 'blur': - case 'change': - this._getDomField(true); - this._updateDomField(); - if (this.field) { - domField.innerHTML = this._escapeHTML(this.field); - } - break; - - case 'input': - this._getDomField(true); - this._updateSchema(); - this._updateDomField(); - this._updateDomValue(); - break; - - case 'keydown': - case 'mousedown': - this.editor.selection = this.editor.getSelection(); - break; - - case 'keyup': - this._getDomField(true); - this._updateDomField(); - break; - - case 'cut': - case 'paste': - setTimeout(function () { - node._getDomField(true); - node._updateDomField(); - }, 1); - break; - } - } - - // focus - // when clicked in whitespace left or right from the field or value, set focus - var domTree = dom.tree; - if (target == domTree.parentNode && type == 'click' && !event.hasMoved) { - var left = (event.offsetX != undefined) ? - (event.offsetX < (this.getLevel() + 1) * 24) : - (event.pageX < util.getAbsoluteLeft(dom.tdSeparator));// for FF - if (left || expandable) { - // node is expandable when it is an object or array - if (domField) { - util.setEndOfContentEditable(domField); - domField.focus(); - } - } - else { - if (domValue && !this.enum) { - util.setEndOfContentEditable(domValue); - domValue.focus(); - } - } - } - if (((target == dom.tdExpand && !expandable) || target == dom.tdField || target == dom.tdSeparator) && - (type == 'click' && !event.hasMoved)) { - if (domField) { - util.setEndOfContentEditable(domField); - domField.focus(); - } - } - - if (type == 'keydown') { - this.onKeyDown(event); - } - }; - - /** - * Key down event handler - * @param {Event} event - */ - Node.prototype.onKeyDown = function (event) { - var keynum = event.which || event.keyCode; - var target = event.target || event.srcElement; - var ctrlKey = event.ctrlKey; - var shiftKey = event.shiftKey; - var altKey = event.altKey; - var handled = false; - var prevNode, nextNode, nextDom, nextDom2; - var editable = this.editor.options.mode === 'tree'; - var oldSelection; - var oldBeforeNode; - var nodes; - var multiselection; - var selectedNodes = this.editor.multiselection.nodes.length > 0 - ? this.editor.multiselection.nodes - : [this]; - var firstNode = selectedNodes[0]; - var lastNode = selectedNodes[selectedNodes.length - 1]; - - // console.log(ctrlKey, keynum, event.charCode); // TODO: cleanup - if (keynum == 13) { // Enter - if (target == this.dom.value) { - if (!this.editable.value || event.ctrlKey) { - if (util.isUrl(this.value)) { - window.open(this.value, '_blank'); - handled = true; - } - } - } - else if (target == this.dom.expand) { - var expandable = this._hasChilds(); - if (expandable) { - var recurse = event.ctrlKey; // with ctrl-key, expand/collapse all - this._onExpand(recurse); - target.focus(); - handled = true; - } - } - } - else if (keynum == 68) { // D - if (ctrlKey && editable) { // Ctrl+D - Node.onDuplicate(selectedNodes); - handled = true; - } - } - else if (keynum == 69) { // E - if (ctrlKey) { // Ctrl+E and Ctrl+Shift+E - this._onExpand(shiftKey); // recurse = shiftKey - target.focus(); // TODO: should restore focus in case of recursing expand (which takes DOM offline) - handled = true; - } - } - else if (keynum == 77 && editable) { // M - if (ctrlKey) { // Ctrl+M - this.showContextMenu(target); - handled = true; - } - } - else if (keynum == 46 && editable) { // Del - if (ctrlKey) { // Ctrl+Del - Node.onRemove(selectedNodes); - handled = true; - } - } - else if (keynum == 45 && editable) { // Ins - if (ctrlKey && !shiftKey) { // Ctrl+Ins - this._onInsertBefore(); - handled = true; - } - else if (ctrlKey && shiftKey) { // Ctrl+Shift+Ins - this._onInsertAfter(); - handled = true; - } - } - else if (keynum == 35) { // End - if (altKey) { // Alt+End - // find the last node - var endNode = this._lastNode(); - if (endNode) { - endNode.focus(Node.focusElement || this._getElementName(target)); - } - handled = true; - } - } - else if (keynum == 36) { // Home - if (altKey) { // Alt+Home - // find the first node - var homeNode = this._firstNode(); - if (homeNode) { - homeNode.focus(Node.focusElement || this._getElementName(target)); - } - handled = true; - } - } - else if (keynum == 37) { // Arrow Left - if (altKey && !shiftKey) { // Alt + Arrow Left - // move to left element - var prevElement = this._previousElement(target); - if (prevElement) { - this.focus(this._getElementName(prevElement)); - } - handled = true; - } - else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow left - if (lastNode.expanded) { - var appendDom = lastNode.getAppend(); - nextDom = appendDom ? appendDom.nextSibling : undefined; - } - else { - var dom = lastNode.getDom(); - nextDom = dom.nextSibling; - } - if (nextDom) { - nextNode = Node.getNodeFromTarget(nextDom); - nextDom2 = nextDom.nextSibling; - nextNode2 = Node.getNodeFromTarget(nextDom2); - if (nextNode && nextNode instanceof AppendNode && - !(lastNode.parent.childs.length == 1) && - nextNode2 && nextNode2.parent) { - oldSelection = this.editor.getSelection(); - oldBeforeNode = lastNode._nextSibling(); - - selectedNodes.forEach(function (node) { - nextNode2.parent.moveBefore(node, nextNode2); - }); - this.focus(Node.focusElement || this._getElementName(target)); - - this.editor._onAction('moveNodes', { - nodes: selectedNodes, - oldBeforeNode: oldBeforeNode, - newBeforeNode: nextNode2, - oldSelection: oldSelection, - newSelection: this.editor.getSelection() - }); - } - } - } - } - else if (keynum == 38) { // Arrow Up - if (altKey && !shiftKey) { // Alt + Arrow Up - // find the previous node - prevNode = this._previousNode(); - if (prevNode) { - this.editor.deselect(true); - prevNode.focus(Node.focusElement || this._getElementName(target)); - } - handled = true; - } - else if (!altKey && ctrlKey && shiftKey && editable) { // Ctrl + Shift + Arrow Up - // select multiple nodes - prevNode = this._previousNode(); - if (prevNode) { - multiselection = this.editor.multiselection; - multiselection.start = multiselection.start || this; - multiselection.end = prevNode; - nodes = this.editor._findTopLevelNodes(multiselection.start, multiselection.end); - - this.editor.select(nodes); - prevNode.focus('field'); // select field as we know this always exists - } - handled = true; - } - else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Up - // find the previous node - prevNode = firstNode._previousNode(); - if (prevNode && prevNode.parent) { - oldSelection = this.editor.getSelection(); - oldBeforeNode = lastNode._nextSibling(); - - selectedNodes.forEach(function (node) { - prevNode.parent.moveBefore(node, prevNode); - }); - this.focus(Node.focusElement || this._getElementName(target)); - - this.editor._onAction('moveNodes', { - nodes: selectedNodes, - oldBeforeNode: oldBeforeNode, - newBeforeNode: prevNode, - oldSelection: oldSelection, - newSelection: this.editor.getSelection() - }); - } - handled = true; - } - } - else if (keynum == 39) { // Arrow Right - if (altKey && !shiftKey) { // Alt + Arrow Right - // move to right element - var nextElement = this._nextElement(target); - if (nextElement) { - this.focus(this._getElementName(nextElement)); - } - handled = true; - } - else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Right - dom = firstNode.getDom(); - var prevDom = dom.previousSibling; - if (prevDom) { - prevNode = Node.getNodeFromTarget(prevDom); - if (prevNode && prevNode.parent && - (prevNode instanceof AppendNode) - && !prevNode.isVisible()) { - oldSelection = this.editor.getSelection(); - oldBeforeNode = lastNode._nextSibling(); - - selectedNodes.forEach(function (node) { - prevNode.parent.moveBefore(node, prevNode); - }); - this.focus(Node.focusElement || this._getElementName(target)); - - this.editor._onAction('moveNodes', { - nodes: selectedNodes, - oldBeforeNode: oldBeforeNode, - newBeforeNode: prevNode, - oldSelection: oldSelection, - newSelection: this.editor.getSelection() - }); - } - } - } - } - else if (keynum == 40) { // Arrow Down - if (altKey && !shiftKey) { // Alt + Arrow Down - // find the next node - nextNode = this._nextNode(); - if (nextNode) { - this.editor.deselect(true); - nextNode.focus(Node.focusElement || this._getElementName(target)); - } - handled = true; - } - else if (!altKey && ctrlKey && shiftKey && editable) { // Ctrl + Shift + Arrow Down - // select multiple nodes - nextNode = this._nextNode(); - if (nextNode) { - multiselection = this.editor.multiselection; - multiselection.start = multiselection.start || this; - multiselection.end = nextNode; - nodes = this.editor._findTopLevelNodes(multiselection.start, multiselection.end); - - this.editor.select(nodes); - nextNode.focus('field'); // select field as we know this always exists - } - handled = true; - } - else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Down - // find the 2nd next node and move before that one - if (lastNode.expanded) { - nextNode = lastNode.append ? lastNode.append._nextNode() : undefined; - } - else { - nextNode = lastNode._nextNode(); - } - var nextNode2 = nextNode && (nextNode._nextNode() || nextNode.parent.append); - if (nextNode2 && nextNode2.parent) { - oldSelection = this.editor.getSelection(); - oldBeforeNode = lastNode._nextSibling(); - - selectedNodes.forEach(function (node) { - nextNode2.parent.moveBefore(node, nextNode2); - }); - this.focus(Node.focusElement || this._getElementName(target)); - - this.editor._onAction('moveNodes', { - nodes: selectedNodes, - oldBeforeNode: oldBeforeNode, - newBeforeNode: nextNode2, - oldSelection: oldSelection, - newSelection: this.editor.getSelection() - }); - } - handled = true; - } - } - - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - }; - - /** - * Handle the expand event, when clicked on the expand button - * @param {boolean} recurse If true, child nodes will be expanded too - * @private - */ - Node.prototype._onExpand = function (recurse) { - if (recurse) { - // Take the table offline - var table = this.dom.tr.parentNode; // TODO: not nice to access the main table like this - var frame = table.parentNode; - var scrollTop = frame.scrollTop; - frame.removeChild(table); - } - - if (this.expanded) { - this.collapse(recurse); - } - else { - this.expand(recurse); - } - - if (recurse) { - // Put the table online again - frame.appendChild(table); - frame.scrollTop = scrollTop; - } - }; - - /** - * Remove nodes - * @param {Node[] | Node} nodes - */ - Node.onRemove = function(nodes) { - if (!Array.isArray(nodes)) { - return Node.onRemove([nodes]); - } - - if (nodes && nodes.length > 0) { - var firstNode = nodes[0]; - var parent = firstNode.parent; - var editor = firstNode.editor; - var firstIndex = firstNode.getIndex(); - editor.highlighter.unhighlight(); - - // adjust the focus - var oldSelection = editor.getSelection(); - Node.blurNodes(nodes); - var newSelection = editor.getSelection(); - - // remove the nodes - nodes.forEach(function (node) { - node.parent._remove(node); - }); - - // store history action - editor._onAction('removeNodes', { - nodes: nodes.slice(0), // store a copy of the array! - parent: parent, - index: firstIndex, - oldSelection: oldSelection, - newSelection: newSelection - }); - } - }; - - - /** - * Duplicate nodes - * duplicated nodes will be added right after the original nodes - * @param {Node[] | Node} nodes - */ - Node.onDuplicate = function(nodes) { - if (!Array.isArray(nodes)) { - return Node.onDuplicate([nodes]); - } - - if (nodes && nodes.length > 0) { - var lastNode = nodes[nodes.length - 1]; - var parent = lastNode.parent; - var editor = lastNode.editor; - - editor.deselect(editor.multiselection.nodes); - - // duplicate the nodes - var oldSelection = editor.getSelection(); - var afterNode = lastNode; - var clones = nodes.map(function (node) { - var clone = node.clone(); - parent.insertAfter(clone, afterNode); - afterNode = clone; - return clone; - }); - - // set selection to the duplicated nodes - if (nodes.length === 1) { - clones[0].focus(); - } - else { - editor.select(clones); - } - var newSelection = editor.getSelection(); - - editor._onAction('duplicateNodes', { - afterNode: lastNode, - nodes: clones, - parent: parent, - oldSelection: oldSelection, - newSelection: newSelection - }); - } - }; - - /** - * Handle insert before event - * @param {String} [field] - * @param {*} [value] - * @param {String} [type] Can be 'auto', 'array', 'object', or 'string' - * @private - */ - Node.prototype._onInsertBefore = function (field, value, type) { - var oldSelection = this.editor.getSelection(); - - var newNode = new Node(this.editor, { - field: (field != undefined) ? field : '', - value: (value != undefined) ? value : '', - type: type - }); - newNode.expand(true); - this.parent.insertBefore(newNode, this); - this.editor.highlighter.unhighlight(); - newNode.focus('field'); - var newSelection = this.editor.getSelection(); - - this.editor._onAction('insertBeforeNodes', { - nodes: [newNode], - beforeNode: this, - parent: this.parent, - oldSelection: oldSelection, - newSelection: newSelection - }); - }; - - /** - * Handle insert after event - * @param {String} [field] - * @param {*} [value] - * @param {String} [type] Can be 'auto', 'array', 'object', or 'string' - * @private - */ - Node.prototype._onInsertAfter = function (field, value, type) { - var oldSelection = this.editor.getSelection(); - - var newNode = new Node(this.editor, { - field: (field != undefined) ? field : '', - value: (value != undefined) ? value : '', - type: type - }); - newNode.expand(true); - this.parent.insertAfter(newNode, this); - this.editor.highlighter.unhighlight(); - newNode.focus('field'); - var newSelection = this.editor.getSelection(); - - this.editor._onAction('insertAfterNodes', { - nodes: [newNode], - afterNode: this, - parent: this.parent, - oldSelection: oldSelection, - newSelection: newSelection - }); - }; - - /** - * Handle append event - * @param {String} [field] - * @param {*} [value] - * @param {String} [type] Can be 'auto', 'array', 'object', or 'string' - * @private - */ - Node.prototype._onAppend = function (field, value, type) { - var oldSelection = this.editor.getSelection(); - - var newNode = new Node(this.editor, { - field: (field != undefined) ? field : '', - value: (value != undefined) ? value : '', - type: type - }); - newNode.expand(true); - this.parent.appendChild(newNode); - this.editor.highlighter.unhighlight(); - newNode.focus('field'); - var newSelection = this.editor.getSelection(); - - this.editor._onAction('appendNodes', { - nodes: [newNode], - parent: this.parent, - oldSelection: oldSelection, - newSelection: newSelection - }); - }; - - /** - * Change the type of the node's value - * @param {String} newType - * @private - */ - Node.prototype._onChangeType = function (newType) { - var oldType = this.type; - if (newType != oldType) { - var oldSelection = this.editor.getSelection(); - this.changeType(newType); - var newSelection = this.editor.getSelection(); - - this.editor._onAction('changeType', { - node: this, - oldType: oldType, - newType: newType, - oldSelection: oldSelection, - newSelection: newSelection - }); - } - }; - - /** - * Sort the child's of the node. Only applicable when the node has type 'object' - * or 'array'. - * @param {String} direction Sorting direction. Available values: "asc", "desc" - * @private - */ - Node.prototype.sort = function (direction) { - if (!this._hasChilds()) { - return; - } - - var order = (direction == 'desc') ? -1 : 1; - var prop = (this.type == 'array') ? 'value': 'field'; - this.hideChilds(); - - var oldChilds = this.childs; - var oldSortOrder = this.sortOrder; - - // copy the array (the old one will be kept for an undo action - this.childs = this.childs.concat(); - - // sort the arrays - this.childs.sort(function (a, b) { - return order * naturalSort(a[prop], b[prop]); - }); - this.sortOrder = (order == 1) ? 'asc' : 'desc'; - - this.editor._onAction('sort', { - node: this, - oldChilds: oldChilds, - oldSort: oldSortOrder, - newChilds: this.childs, - newSort: this.sortOrder - }); - - this.showChilds(); - }; - - /** - * Create a table row with an append button. - * @return {HTMLElement | undefined} buttonAppend or undefined when inapplicable - */ - Node.prototype.getAppend = function () { - if (!this.append) { - this.append = new AppendNode(this.editor); - this.append.setParent(this); - } - return this.append.getDom(); - }; - - /** - * Find the node from an event target - * @param {Node} target - * @return {Node | undefined} node or undefined when not found - * @static - */ - Node.getNodeFromTarget = function (target) { - while (target) { - if (target.node) { - return target.node; - } - target = target.parentNode; - } - - return undefined; - }; - - /** - * Remove the focus of given nodes, and move the focus to the (a) node before, - * (b) the node after, or (c) the parent node. - * @param {Array. | Node} nodes - */ - Node.blurNodes = function (nodes) { - if (!Array.isArray(nodes)) { - Node.blurNodes([nodes]); - return; - } - - var firstNode = nodes[0]; - var parent = firstNode.parent; - var firstIndex = firstNode.getIndex(); - - if (parent.childs[firstIndex + nodes.length]) { - parent.childs[firstIndex + nodes.length].focus(); - } - else if (parent.childs[firstIndex - 1]) { - parent.childs[firstIndex - 1].focus(); - } - else { - parent.focus(); - } - }; - - /** - * Get the next sibling of current node - * @return {Node} nextSibling - * @private - */ - Node.prototype._nextSibling = function () { - var index = this.parent.childs.indexOf(this); - return this.parent.childs[index + 1] || this.parent.append; - }; - - /** - * Get the previously rendered node - * @return {Node | null} previousNode - * @private - */ - Node.prototype._previousNode = function () { - var prevNode = null; - var dom = this.getDom(); - if (dom && dom.parentNode) { - // find the previous field - var prevDom = dom; - do { - prevDom = prevDom.previousSibling; - prevNode = Node.getNodeFromTarget(prevDom); - } - while (prevDom && (prevNode instanceof AppendNode && !prevNode.isVisible())); - } - return prevNode; - }; - - /** - * Get the next rendered node - * @return {Node | null} nextNode - * @private - */ - Node.prototype._nextNode = function () { - var nextNode = null; - var dom = this.getDom(); - if (dom && dom.parentNode) { - // find the previous field - var nextDom = dom; - do { - nextDom = nextDom.nextSibling; - nextNode = Node.getNodeFromTarget(nextDom); - } - while (nextDom && (nextNode instanceof AppendNode && !nextNode.isVisible())); - } - - return nextNode; - }; - - /** - * Get the first rendered node - * @return {Node | null} firstNode - * @private - */ - Node.prototype._firstNode = function () { - var firstNode = null; - var dom = this.getDom(); - if (dom && dom.parentNode) { - var firstDom = dom.parentNode.firstChild; - firstNode = Node.getNodeFromTarget(firstDom); - } - - return firstNode; - }; - - /** - * Get the last rendered node - * @return {Node | null} lastNode - * @private - */ - Node.prototype._lastNode = function () { - var lastNode = null; - var dom = this.getDom(); - if (dom && dom.parentNode) { - var lastDom = dom.parentNode.lastChild; - lastNode = Node.getNodeFromTarget(lastDom); - while (lastDom && (lastNode instanceof AppendNode && !lastNode.isVisible())) { - lastDom = lastDom.previousSibling; - lastNode = Node.getNodeFromTarget(lastDom); - } - } - return lastNode; - }; - - /** - * Get the next element which can have focus. - * @param {Element} elem - * @return {Element | null} nextElem - * @private - */ - Node.prototype._previousElement = function (elem) { - var dom = this.dom; - // noinspection FallthroughInSwitchStatementJS - switch (elem) { - case dom.value: - if (this.fieldEditable) { - return dom.field; - } - // intentional fall through - case dom.field: - if (this._hasChilds()) { - return dom.expand; - } - // intentional fall through - case dom.expand: - return dom.menu; - case dom.menu: - if (dom.drag) { - return dom.drag; - } - // intentional fall through - default: - return null; - } - }; - - /** - * Get the next element which can have focus. - * @param {Element} elem - * @return {Element | null} nextElem - * @private - */ - Node.prototype._nextElement = function (elem) { - var dom = this.dom; - // noinspection FallthroughInSwitchStatementJS - switch (elem) { - case dom.drag: - return dom.menu; - case dom.menu: - if (this._hasChilds()) { - return dom.expand; - } - // intentional fall through - case dom.expand: - if (this.fieldEditable) { - return dom.field; - } - // intentional fall through - case dom.field: - if (!this._hasChilds()) { - return dom.value; - } - default: - return null; - } - }; - - /** - * Get the dom name of given element. returns null if not found. - * For example when element == dom.field, "field" is returned. - * @param {Element} element - * @return {String | null} elementName Available elements with name: 'drag', - * 'menu', 'expand', 'field', 'value' - * @private - */ - Node.prototype._getElementName = function (element) { - var dom = this.dom; - for (var name in dom) { - if (dom.hasOwnProperty(name)) { - if (dom[name] == element) { - return name; - } - } - } - return null; - }; - - /** - * Test if this node has childs. This is the case when the node is an object - * or array. - * @return {boolean} hasChilds - * @private - */ - Node.prototype._hasChilds = function () { - return this.type == 'array' || this.type == 'object'; - }; - - // titles with explanation for the different types - Node.TYPE_TITLES = { - 'auto': 'Field type "auto". ' + - 'The field type is automatically determined from the value ' + - 'and can be a string, number, boolean, or null.', - 'object': 'Field type "object". ' + - 'An object contains an unordered set of key/value pairs.', - 'array': 'Field type "array". ' + - 'An array contains an ordered collection of values.', - 'string': 'Field type "string". ' + - 'Field type is not determined from the value, ' + - 'but always returned as string.' - }; - - /** - * Show a contextmenu for this node - * @param {HTMLElement} anchor Anchor element to attach the context menu to - * as sibling. - * @param {function} [onClose] Callback method called when the context menu - * is being closed. - */ - Node.prototype.showContextMenu = function (anchor, onClose) { - var node = this; - var titles = Node.TYPE_TITLES; - var items = []; - - if (this.editable.value) { - items.push({ - text: 'Type', - title: 'Change the type of this field', - className: 'jsoneditor-type-' + this.type, - submenu: [ - { - text: 'Auto', - className: 'jsoneditor-type-auto' + - (this.type == 'auto' ? ' jsoneditor-selected' : ''), - title: titles.auto, - click: function () { - node._onChangeType('auto'); - } - }, - { - text: 'Array', - className: 'jsoneditor-type-array' + - (this.type == 'array' ? ' jsoneditor-selected' : ''), - title: titles.array, - click: function () { - node._onChangeType('array'); - } - }, - { - text: 'Object', - className: 'jsoneditor-type-object' + - (this.type == 'object' ? ' jsoneditor-selected' : ''), - title: titles.object, - click: function () { - node._onChangeType('object'); - } - }, - { - text: 'String', - className: 'jsoneditor-type-string' + - (this.type == 'string' ? ' jsoneditor-selected' : ''), - title: titles.string, - click: function () { - node._onChangeType('string'); - } - } - ] - }); - } - - if (this._hasChilds()) { - var direction = ((this.sortOrder == 'asc') ? 'desc': 'asc'); - items.push({ - text: 'Sort', - title: 'Sort the childs of this ' + this.type, - className: 'jsoneditor-sort-' + direction, - click: function () { - node.sort(direction); - }, - submenu: [ - { - text: 'Ascending', - className: 'jsoneditor-sort-asc', - title: 'Sort the childs of this ' + this.type + ' in ascending order', - click: function () { - node.sort('asc'); - } - }, - { - text: 'Descending', - className: 'jsoneditor-sort-desc', - title: 'Sort the childs of this ' + this.type +' in descending order', - click: function () { - node.sort('desc'); - } - } - ] - }); - } - - if (this.parent && this.parent._hasChilds()) { - if (items.length) { - // create a separator - items.push({ - 'type': 'separator' - }); - } - - // create append button (for last child node only) - var childs = node.parent.childs; - if (node == childs[childs.length - 1]) { - items.push({ - text: 'Append', - title: 'Append a new field with type \'auto\' after this field (Ctrl+Shift+Ins)', - submenuTitle: 'Select the type of the field to be appended', - className: 'jsoneditor-append', - click: function () { - node._onAppend('', '', 'auto'); - }, - submenu: [ - { - text: 'Auto', - className: 'jsoneditor-type-auto', - title: titles.auto, - click: function () { - node._onAppend('', '', 'auto'); - } - }, - { - text: 'Array', - className: 'jsoneditor-type-array', - title: titles.array, - click: function () { - node._onAppend('', []); - } - }, - { - text: 'Object', - className: 'jsoneditor-type-object', - title: titles.object, - click: function () { - node._onAppend('', {}); - } - }, - { - text: 'String', - className: 'jsoneditor-type-string', - title: titles.string, - click: function () { - node._onAppend('', '', 'string'); - } - } - ] - }); - } - - // create insert button - items.push({ - text: 'Insert', - title: 'Insert a new field with type \'auto\' before this field (Ctrl+Ins)', - submenuTitle: 'Select the type of the field to be inserted', - className: 'jsoneditor-insert', - click: function () { - node._onInsertBefore('', '', 'auto'); - }, - submenu: [ - { - text: 'Auto', - className: 'jsoneditor-type-auto', - title: titles.auto, - click: function () { - node._onInsertBefore('', '', 'auto'); - } - }, - { - text: 'Array', - className: 'jsoneditor-type-array', - title: titles.array, - click: function () { - node._onInsertBefore('', []); - } - }, - { - text: 'Object', - className: 'jsoneditor-type-object', - title: titles.object, - click: function () { - node._onInsertBefore('', {}); - } - }, - { - text: 'String', - className: 'jsoneditor-type-string', - title: titles.string, - click: function () { - node._onInsertBefore('', '', 'string'); - } - } - ] - }); - - if (this.editable.field) { - // create duplicate button - items.push({ - text: 'Duplicate', - title: 'Duplicate this field (Ctrl+D)', - className: 'jsoneditor-duplicate', - click: function () { - Node.onDuplicate(node); - } - }); - - // create remove button - items.push({ - text: 'Remove', - title: 'Remove this field (Ctrl+Del)', - className: 'jsoneditor-remove', - click: function () { - Node.onRemove(node); - } - }); - } - } - - var menu = new ContextMenu(items, {close: onClose}); - menu.show(anchor, this.editor.content); - }; - - /** - * get the type of a value - * @param {*} value - * @return {String} type Can be 'object', 'array', 'string', 'auto' - * @private - */ - Node.prototype._getType = function(value) { - if (value instanceof Array) { - return 'array'; - } - if (value instanceof Object) { - return 'object'; - } - if (typeof(value) == 'string' && typeof(this._stringCast(value)) != 'string') { - return 'string'; - } - - return 'auto'; - }; - - /** - * cast contents of a string to the correct type. This can be a string, - * a number, a boolean, etc - * @param {String} str - * @return {*} castedStr - * @private - */ - Node.prototype._stringCast = function(str) { - var lower = str.toLowerCase(), - num = Number(str), // will nicely fail with '123ab' - numFloat = parseFloat(str); // will nicely fail with ' ' - - if (str == '') { - return ''; - } - else if (lower == 'null') { - return null; - } - else if (lower == 'true') { - return true; - } - else if (lower == 'false') { - return false; - } - else if (!isNaN(num) && !isNaN(numFloat)) { - return num; - } - else { - return str; - } - }; - - /** - * escape a text, such that it can be displayed safely in an HTML element - * @param {String} text - * @return {String} escapedText - * @private - */ - Node.prototype._escapeHTML = function (text) { - if (typeof text !== 'string') { - return String(text); - } - else { - var htmlEscaped = String(text) - .replace(/&/g, '&') // must be replaced first! - .replace(//g, '>') - .replace(/ /g, '  ') // replace double space with an nbsp and space - .replace(/^ /, ' ') // space at start - .replace(/ $/, ' '); // space at end - - var json = JSON.stringify(htmlEscaped); - var html = json.substring(1, json.length - 1); - if (this.editor.options.escapeUnicode === true) { - html = util.escapeUnicodeChars(html); - } - return html; - } - }; - - /** - * unescape a string. - * @param {String} escapedText - * @return {String} text - * @private - */ - Node.prototype._unescapeHTML = function (escapedText) { - var json = '"' + this._escapeJSON(escapedText) + '"'; - var htmlEscaped = util.parse(json); - - return htmlEscaped - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/ |\u00A0/g, ' ') - .replace(/&/g, '&'); // must be replaced last - }; - - /** - * escape a text to make it a valid JSON string. The method will: - * - replace unescaped double quotes with '\"' - * - replace unescaped backslash with '\\' - * - replace returns with '\n' - * @param {String} text - * @return {String} escapedText - * @private - */ - Node.prototype._escapeJSON = function (text) { - // TODO: replace with some smart regex (only when a new solution is faster!) - var escaped = ''; - var i = 0; - while (i < text.length) { - var c = text.charAt(i); - if (c == '\n') { - escaped += '\\n'; - } - else if (c == '\\') { - escaped += c; - i++; - - c = text.charAt(i); - if (c === '' || '"\\/bfnrtu'.indexOf(c) == -1) { - escaped += '\\'; // no valid escape character - } - escaped += c; - } - else if (c == '"') { - escaped += '\\"'; - } - else { - escaped += c; - } - i++; - } - - return escaped; - }; - - // TODO: find a nicer solution to resolve this circular dependency between Node and AppendNode - var AppendNode = appendNodeFactory(Node); - - module.exports = Node; - - -/***/ }, -/* 9 */ -/***/ function(module, exports) { - - /* - * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license - * Author: Jim Palmer (based on chunking idea from Dave Koelle) - */ - /*jshint unused:false */ - module.exports = function naturalSort (a, b) { - "use strict"; - var re = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, - sre = /(^[ ]*|[ ]*$)/g, - dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, - hre = /^0x[0-9a-f]+$/i, - ore = /^0/, - i = function(s) { return naturalSort.insensitive && ('' + s).toLowerCase() || '' + s; }, - // convert all to strings strip whitespace - x = i(a).replace(sre, '') || '', - y = i(b).replace(sre, '') || '', - // chunk/tokenize - xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), - yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), - // numeric, hex or date detection - xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && x.match(dre) && Date.parse(x)), - yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null, - oFxNcL, oFyNcL; - // first try and sort Hex codes or Dates - if (yD) { - if ( xD < yD ) { return -1; } - else if ( xD > yD ) { return 1; } - } - // natural sorting through split numeric strings and default strings - for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { - // find floats not starting with '0', string or 0 if not defined (Clint Priest) - oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; - oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; - // handle numeric vs string comparison - number < string - (Kyle Adams) - if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; } - // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' - else if (typeof oFxNcL !== typeof oFyNcL) { - oFxNcL += ''; - oFyNcL += ''; - } - if (oFxNcL < oFyNcL) { return -1; } - if (oFxNcL > oFyNcL) { return 1; } - } - return 0; - }; - - -/***/ }, -/* 10 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var util = __webpack_require__(4); - var ContextMenu = __webpack_require__(7); - - /** - * A factory function to create an AppendNode, which depends on a Node - * @param {Node} Node - */ - function appendNodeFactory(Node) { - /** - * @constructor AppendNode - * @extends Node - * @param {TreeEditor} editor - * Create a new AppendNode. This is a special node which is created at the - * end of the list with childs for an object or array - */ - function AppendNode (editor) { - /** @type {TreeEditor} */ - this.editor = editor; - this.dom = {}; - } - - AppendNode.prototype = new Node(); - - /** - * Return a table row with an append button. - * @return {Element} dom TR element - */ - AppendNode.prototype.getDom = function () { - // TODO: implement a new solution for the append node - var dom = this.dom; - - if (dom.tr) { - return dom.tr; - } - - this._updateEditability(); - - // a row for the append button - var trAppend = document.createElement('tr'); - trAppend.node = this; - dom.tr = trAppend; - - // TODO: consistent naming - - if (this.editable.field) { - // a cell for the dragarea column - dom.tdDrag = document.createElement('td'); - - // create context menu - var tdMenu = document.createElement('td'); - dom.tdMenu = tdMenu; - var menu = document.createElement('button'); - menu.className = 'jsoneditor-contextmenu'; - menu.title = 'Click to open the actions menu (Ctrl+M)'; - dom.menu = menu; - tdMenu.appendChild(dom.menu); - } - - // a cell for the contents (showing text 'empty') - var tdAppend = document.createElement('td'); - var domText = document.createElement('div'); - domText.innerHTML = '(empty)'; - domText.className = 'jsoneditor-readonly'; - tdAppend.appendChild(domText); - dom.td = tdAppend; - dom.text = domText; - - this.updateDom(); - - return trAppend; - }; - - /** - * Update the HTML dom of the Node - */ - AppendNode.prototype.updateDom = function () { - var dom = this.dom; - var tdAppend = dom.td; - if (tdAppend) { - tdAppend.style.paddingLeft = (this.getLevel() * 24 + 26) + 'px'; - // TODO: not so nice hard coded offset - } - - var domText = dom.text; - if (domText) { - domText.innerHTML = '(empty ' + this.parent.type + ')'; - } - - // attach or detach the contents of the append node: - // hide when the parent has childs, show when the parent has no childs - var trAppend = dom.tr; - if (!this.isVisible()) { - if (dom.tr.firstChild) { - if (dom.tdDrag) { - trAppend.removeChild(dom.tdDrag); - } - if (dom.tdMenu) { - trAppend.removeChild(dom.tdMenu); - } - trAppend.removeChild(tdAppend); - } - } - else { - if (!dom.tr.firstChild) { - if (dom.tdDrag) { - trAppend.appendChild(dom.tdDrag); - } - if (dom.tdMenu) { - trAppend.appendChild(dom.tdMenu); - } - trAppend.appendChild(tdAppend); - } - } - }; - - /** - * Check whether the AppendNode is currently visible. - * the AppendNode is visible when its parent has no childs (i.e. is empty). - * @return {boolean} isVisible - */ - AppendNode.prototype.isVisible = function () { - return (this.parent.childs.length == 0); - }; - - /** - * Show a contextmenu for this node - * @param {HTMLElement} anchor The element to attach the menu to. - * @param {function} [onClose] Callback method called when the context menu - * is being closed. - */ - AppendNode.prototype.showContextMenu = function (anchor, onClose) { - var node = this; - var titles = Node.TYPE_TITLES; - var items = [ - // create append button - { - 'text': 'Append', - 'title': 'Append a new field with type \'auto\' (Ctrl+Shift+Ins)', - 'submenuTitle': 'Select the type of the field to be appended', - 'className': 'jsoneditor-insert', - 'click': function () { - node._onAppend('', '', 'auto'); - }, - 'submenu': [ - { - 'text': 'Auto', - 'className': 'jsoneditor-type-auto', - 'title': titles.auto, - 'click': function () { - node._onAppend('', '', 'auto'); - } - }, - { - 'text': 'Array', - 'className': 'jsoneditor-type-array', - 'title': titles.array, - 'click': function () { - node._onAppend('', []); - } - }, - { - 'text': 'Object', - 'className': 'jsoneditor-type-object', - 'title': titles.object, - 'click': function () { - node._onAppend('', {}); - } - }, - { - 'text': 'String', - 'className': 'jsoneditor-type-string', - 'title': titles.string, - 'click': function () { - node._onAppend('', '', 'string'); - } - } - ] - } - ]; - - var menu = new ContextMenu(items, {close: onClose}); - menu.show(anchor, this.editor.content); - }; - - /** - * Handle an event. The event is catched centrally by the editor - * @param {Event} event - */ - AppendNode.prototype.onEvent = function (event) { - var type = event.type; - var target = event.target || event.srcElement; - var dom = this.dom; - - // highlight the append nodes parent - var menu = dom.menu; - if (target == menu) { - if (type == 'mouseover') { - this.editor.highlighter.highlight(this.parent); - } - else if (type == 'mouseout') { - this.editor.highlighter.unhighlight(); - } - } - - // context menu events - if (type == 'click' && target == dom.menu) { - var highlighter = this.editor.highlighter; - highlighter.highlight(this.parent); - highlighter.lock(); - util.addClassName(dom.menu, 'jsoneditor-selected'); - this.showContextMenu(dom.menu, function () { - util.removeClassName(dom.menu, 'jsoneditor-selected'); - highlighter.unlock(); - highlighter.unhighlight(); - }); - } - - if (type == 'keydown') { - this.onKeyDown(event); - } - }; - - return AppendNode; - } - - module.exports = appendNodeFactory; - - -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var ContextMenu = __webpack_require__(7); - - /** - * Create a select box to be used in the editor menu's, which allows to switch mode - * @param {HTMLElement} container - * @param {String[]} modes Available modes: 'code', 'form', 'text', 'tree', 'view' - * @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view' - * @param {function(mode: string)} onSwitch Callback invoked on switch - * @constructor - */ - function ModeSwitcher(container, modes, current, onSwitch) { - // available modes - var availableModes = { - code: { - 'text': 'Code', - 'title': 'Switch to code highlighter', - 'click': function () { - onSwitch('code') - } - }, - form: { - 'text': 'Form', - 'title': 'Switch to form editor', - 'click': function () { - onSwitch('form'); - } - }, - text: { - 'text': 'Text', - 'title': 'Switch to plain text editor', - 'click': function () { - onSwitch('text'); - } - }, - tree: { - 'text': 'Tree', - 'title': 'Switch to tree editor', - 'click': function () { - onSwitch('tree'); - } - }, - view: { - 'text': 'View', - 'title': 'Switch to tree view', - 'click': function () { - onSwitch('view'); - } - } - }; - - // list the selected modes - var items = []; - for (var i = 0; i < modes.length; i++) { - var mode = modes[i]; - var item = availableModes[mode]; - if (!item) { - throw new Error('Unknown mode "' + mode + '"'); - } - - item.className = 'jsoneditor-type-modes' + ((current == mode) ? ' jsoneditor-selected' : ''); - items.push(item); - } - - // retrieve the title of current mode - var currentMode = availableModes[current]; - if (!currentMode) { - throw new Error('Unknown mode "' + current + '"'); - } - var currentTitle = currentMode.text; - - // create the html element - var box = document.createElement('button'); - box.className = 'jsoneditor-modes jsoneditor-separator'; - box.innerHTML = currentTitle + ' ▾'; - box.title = 'Switch editor mode'; - box.onclick = function () { - var menu = new ContextMenu(items); - menu.show(box); - }; - - var frame = document.createElement('div'); - frame.className = 'jsoneditor-modes'; - frame.style.position = 'relative'; - frame.appendChild(box); - - container.appendChild(frame); - - this.dom = { - container: container, - box: box, - frame: frame - }; - } - - /** - * Set focus to switcher - */ - ModeSwitcher.prototype.focus = function () { - this.dom.box.focus(); - }; - - /** - * Destroy the ModeSwitcher, remove from DOM - */ - ModeSwitcher.prototype.destroy = function () { - if (this.dom && this.dom.frame && this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } - this.dom = null; - }; - - module.exports = ModeSwitcher; - - -/***/ }, -/* 12 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var ace; - try { - ace = __webpack_require__(13); - } - catch (err) { - // failed to load ace, no problem, we will fall back to plain text - } - - var ModeSwitcher = __webpack_require__(11); - var util = __webpack_require__(4); - - // create a mixin with the functions for text mode - var textmode = {}; - - var MAX_ERRORS = 3; // maximum number of displayed errors at the bottom - - /** - * Create a text editor - * @param {Element} container - * @param {Object} [options] Object with options. available options: - * {String} mode Available values: - * "text" (default) - * or "code". - * {Number} indentation Number of indentation - * spaces. 2 by default. - * {function} onChange Callback method - * triggered on change - * {function} onModeChange Callback method - * triggered after setMode - * {Object} ace A custom instance of - * Ace editor. - * {boolean} escapeUnicode If true, unicode - * characters are escaped. - * false by default. - * @private - */ - textmode.create = function (container, options) { - // read options - options = options || {}; - this.options = options; - - // indentation - if (options.indentation) { - this.indentation = Number(options.indentation); - } - else { - this.indentation = 2; // number of spaces - } - - // grab ace from options if provided - var _ace = options.ace ? options.ace : ace; - - // determine mode - this.mode = (options.mode == 'code') ? 'code' : 'text'; - if (this.mode == 'code') { - // verify whether Ace editor is available and supported - if (typeof _ace === 'undefined') { - this.mode = 'text'; - console.warn('Failed to load Ace editor, falling back to plain text mode. Please use a JSONEditor bundle including Ace, or pass Ace as via the configuration option `ace`.'); - } - } - - // determine theme - this.theme = options.theme || 'ace/theme/jsoneditor'; - - var me = this; - this.container = container; - this.dom = {}; - this.aceEditor = undefined; // ace code editor - this.textarea = undefined; // plain text editor (fallback when Ace is not available) - this.validateSchema = null; - - // create a debounced validate function - this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL); - - this.width = container.clientWidth; - this.height = container.clientHeight; - - this.frame = document.createElement('div'); - this.frame.className = 'jsoneditor jsoneditor-mode-' + this.options.mode; - this.frame.onclick = function (event) { - // prevent default submit action when the editor is located inside a form - event.preventDefault(); - }; - this.frame.onkeydown = function (event) { - me._onKeyDown(event); - }; - - // create menu - this.menu = document.createElement('div'); - this.menu.className = 'jsoneditor-menu'; - this.frame.appendChild(this.menu); - - // create format button - var buttonFormat = document.createElement('button'); - buttonFormat.className = 'jsoneditor-format'; - buttonFormat.title = 'Format JSON data, with proper indentation and line feeds (Ctrl+\\)'; - this.menu.appendChild(buttonFormat); - buttonFormat.onclick = function () { - try { - me.format(); - me._onChange(); - } - catch (err) { - me._onError(err); - } - }; - - // create compact button - var buttonCompact = document.createElement('button'); - buttonCompact.className = 'jsoneditor-compact'; - buttonCompact.title = 'Compact JSON data, remove all whitespaces (Ctrl+Shift+\\)'; - this.menu.appendChild(buttonCompact); - buttonCompact.onclick = function () { - try { - me.compact(); - me._onChange(); - } - catch (err) { - me._onError(err); - } - }; - - // create mode box - if (this.options && this.options.modes && this.options.modes.length) { - this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) { - // switch mode and restore focus - me.setMode(mode); - me.modeSwitcher.focus(); - }); - } - - this.content = document.createElement('div'); - this.content.className = 'jsoneditor-outer'; - this.frame.appendChild(this.content); - - this.container.appendChild(this.frame); - - if (this.mode == 'code') { - this.editorDom = document.createElement('div'); - this.editorDom.style.height = '100%'; // TODO: move to css - this.editorDom.style.width = '100%'; // TODO: move to css - this.content.appendChild(this.editorDom); - - var aceEditor = _ace.edit(this.editorDom); - aceEditor.$blockScrolling = Infinity; - aceEditor.setTheme(this.theme); - aceEditor.setShowPrintMargin(false); - aceEditor.setFontSize(13); - aceEditor.getSession().setMode('ace/mode/json'); - aceEditor.getSession().setTabSize(this.indentation); - aceEditor.getSession().setUseSoftTabs(true); - aceEditor.getSession().setUseWrapMode(true); - aceEditor.commands.bindKey('Ctrl-L', null); // disable Ctrl+L (is used by the browser to select the address bar) - aceEditor.commands.bindKey('Command-L', null); // disable Ctrl+L (is used by the browser to select the address bar) - this.aceEditor = aceEditor; - - // TODO: deprecated since v5.0.0. Cleanup backward compatibility some day - if (!this.hasOwnProperty('editor')) { - Object.defineProperty(this, 'editor', { - get: function () { - console.warn('Property "editor" has been renamed to "aceEditor".'); - return me.aceEditor; - }, - set: function (aceEditor) { - console.warn('Property "editor" has been renamed to "aceEditor".'); - me.aceEditor = aceEditor; - } - }); - } - - var poweredBy = document.createElement('a'); - poweredBy.appendChild(document.createTextNode('powered by ace')); - poweredBy.href = 'http://ace.ajax.org'; - poweredBy.target = '_blank'; - poweredBy.className = 'jsoneditor-poweredBy'; - poweredBy.onclick = function () { - // TODO: this anchor falls below the margin of the content, - // therefore the normal a.href does not work. We use a click event - // for now, but this should be fixed. - window.open(poweredBy.href, poweredBy.target); - }; - this.menu.appendChild(poweredBy); - - // register onchange event - aceEditor.on('change', this._onChange.bind(this)); - } - else { - // load a plain text textarea - var textarea = document.createElement('textarea'); - textarea.className = 'jsoneditor-text'; - textarea.spellcheck = false; - this.content.appendChild(textarea); - this.textarea = textarea; - - // register onchange event - if (this.textarea.oninput === null) { - this.textarea.oninput = this._onChange.bind(this); - } - else { - // oninput is undefined. For IE8- - this.textarea.onchange = this._onChange.bind(this); - } - } - - this.setSchema(this.options.schema); - }; - - /** - * Handle a change: - * - Validate JSON schema - * - Send a callback to the onChange listener if provided - * @private - */ - textmode._onChange = function () { - // validate JSON schema (if configured) - this._debouncedValidate(); - - // trigger the onChange callback - if (this.options.onChange) { - try { - this.options.onChange(); - } - catch (err) { - console.error('Error in onChange callback: ', err); - } - } - }; - - /** - * Event handler for keydown. Handles shortcut keys - * @param {Event} event - * @private - */ - textmode._onKeyDown = function (event) { - var keynum = event.which || event.keyCode; - var handled = false; - - if (keynum == 220 && event.ctrlKey) { - if (event.shiftKey) { // Ctrl+Shift+\ - this.compact(); - this._onChange(); - } - else { // Ctrl+\ - this.format(); - this._onChange(); - } - handled = true; - } - - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - }; - - /** - * Destroy the editor. Clean up DOM, event listeners, and web workers. - */ - textmode.destroy = function () { - // remove old ace editor - if (this.aceEditor) { - this.aceEditor.destroy(); - this.aceEditor = null; - } - - if (this.frame && this.container && this.frame.parentNode == this.container) { - this.container.removeChild(this.frame); - } - - if (this.modeSwitcher) { - this.modeSwitcher.destroy(); - this.modeSwitcher = null; - } - - this.textarea = null; - - this._debouncedValidate = null; - }; - - /** - * Compact the code in the formatter - */ - textmode.compact = function () { - var json = this.get(); - var text = JSON.stringify(json); - this.setText(text); - }; - - /** - * Format the code in the formatter - */ - textmode.format = function () { - var json = this.get(); - var text = JSON.stringify(json, null, this.indentation); - this.setText(text); - }; - - /** - * Set focus to the formatter - */ - textmode.focus = function () { - if (this.textarea) { - this.textarea.focus(); - } - if (this.aceEditor) { - this.aceEditor.focus(); - } - }; - - /** - * Resize the formatter - */ - textmode.resize = function () { - if (this.aceEditor) { - var force = false; - this.aceEditor.resize(force); - } - }; - - /** - * Set json data in the formatter - * @param {Object} json - */ - textmode.set = function(json) { - this.setText(JSON.stringify(json, null, this.indentation)); - }; - - /** - * Get json data from the formatter - * @return {Object} json - */ - textmode.get = function() { - var text = this.getText(); - var json; - - try { - json = util.parse(text); // this can throw an error - } - catch (err) { - // try to sanitize json, replace JavaScript notation with JSON notation - text = util.sanitize(text); - - // try to parse again - json = util.parse(text); // this can throw an error - } - - return json; - }; - - /** - * Get the text contents of the editor - * @return {String} jsonText - */ - textmode.getText = function() { - if (this.textarea) { - return this.textarea.value; - } - if (this.aceEditor) { - return this.aceEditor.getValue(); - } - return ''; - }; - - /** - * Set the text contents of the editor - * @param {String} jsonText - */ - textmode.setText = function(jsonText) { - var text; - - if (this.options.escapeUnicode === true) { - text = util.escapeUnicodeChars(jsonText); - } - else { - text = jsonText; - } - - if (this.textarea) { - this.textarea.value = text; - } - if (this.aceEditor) { - // prevent emitting onChange events while setting new text - var originalOnChange = this.options.onChange; - this.options.onChange = null; - - this.aceEditor.setValue(text, -1); - - this.options.onChange = originalOnChange; - } - - // validate JSON schema - this.validate(); - }; - - /** - * Validate current JSON object against the configured JSON schema - * Throws an exception when no JSON schema is configured - */ - textmode.validate = function () { - // clear all current errors - if (this.dom.validationErrors) { - this.dom.validationErrors.parentNode.removeChild(this.dom.validationErrors); - this.dom.validationErrors = null; - - this.content.style.marginBottom = ''; - this.content.style.paddingBottom = ''; - } - - var doValidate = false; - var errors = []; - var json; - try { - json = this.get(); // this can fail when there is no valid json - doValidate = true; - } - catch (err) { - // no valid JSON, don't validate - } - - // only validate the JSON when parsing the JSON succeeded - if (doValidate && this.validateSchema) { - var valid = this.validateSchema(json); - if (!valid) { - errors = this.validateSchema.errors.map(function (error) { - return util.improveSchemaError(error); - }); - } - } - - if (errors.length > 0) { - // limit the number of displayed errors - var limit = errors.length > MAX_ERRORS; - if (limit) { - errors = errors.slice(0, MAX_ERRORS); - var hidden = this.validateSchema.errors.length - MAX_ERRORS; - errors.push('(' + hidden + ' more errors...)') - } - - var validationErrors = document.createElement('div'); - validationErrors.innerHTML = '' + - '' + - errors.map(function (error) { - var message; - if (typeof error === 'string') { - message = ''; - } - else { - message = '' + - ''; - } - - return '' + message + '' - }).join('') + - '' + - '
' + error + '
' + error.dataPath + '' + error.message + '
'; - - this.dom.validationErrors = validationErrors; - this.frame.appendChild(validationErrors); - - var height = validationErrors.clientHeight; - this.content.style.marginBottom = (-height) + 'px'; - this.content.style.paddingBottom = height + 'px'; - } - - // update the height of the ace editor - if (this.aceEditor) { - var force = false; - this.aceEditor.resize(force); - } - }; - - // define modes - module.exports = [ - { - mode: 'text', - mixin: textmode, - data: 'text', - load: textmode.format - }, - { - mode: 'code', - mixin: textmode, - data: 'text', - load: textmode.format - } - ]; - - -/***/ }, -/* 13 */ -/***/ function(module, exports, __webpack_require__) { - - // load brace - var ace = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module \"brace\""); e.code = 'MODULE_NOT_FOUND'; throw e; }())); - - // load required ace modules - __webpack_require__(14); - __webpack_require__(16); - __webpack_require__(17); - - module.exports = ace; - - -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { - - ace.define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(acequire, exports, module) { - "use strict"; - - var oop = acequire("../lib/oop"); - var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules; - - var JsonHighlightRules = function() { - this.$rules = { - "start" : [ - { - token : "variable", // single line - regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)' - }, { - token : "string", // single line - regex : '"', - next : "string" - }, { - token : "constant.numeric", // hex - regex : "0[xX][0-9a-fA-F]+\\b" - }, { - token : "constant.numeric", // float - regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" - }, { - token : "constant.language.boolean", - regex : "(?:true|false)\\b" - }, { - token : "invalid.illegal", // single quoted strings are not allowed - regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']" - }, { - token : "invalid.illegal", // comments are not allowed - regex : "\\/\\/.*$" - }, { - token : "paren.lparen", - regex : "[[({]" - }, { - token : "paren.rparen", - regex : "[\\])}]" - }, { - token : "text", - regex : "\\s+" - } - ], - "string" : [ - { - token : "constant.language.escape", - regex : /\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/ - }, { - token : "string", - regex : '[^"\\\\]+' - }, { - token : "string", - regex : '"', - next : "start" - }, { - token : "string", - regex : "", - next : "start" - } - ] - }; - - }; - - oop.inherits(JsonHighlightRules, TextHighlightRules); - - exports.JsonHighlightRules = JsonHighlightRules; - }); - - ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(acequire, exports, module) { - "use strict"; - - var Range = acequire("../range").Range; - - var MatchingBraceOutdent = function() {}; - - (function() { - - this.checkOutdent = function(line, input) { - if (! /^\s+$/.test(line)) - return false; - - return /^\s*\}/.test(input); - }; - - this.autoOutdent = function(doc, row) { - var line = doc.getLine(row); - var match = line.match(/^(\s*\})/); - - if (!match) return 0; - - var column = match[1].length; - var openBracePos = doc.findMatchingBracket({row: row, column: column}); - - if (!openBracePos || openBracePos.row == row) return 0; - - var indent = this.$getIndent(doc.getLine(openBracePos.row)); - doc.replace(new Range(row, 0, row, column-1), indent); - }; - - this.$getIndent = function(line) { - return line.match(/^\s*/)[0]; - }; - - }).call(MatchingBraceOutdent.prototype); - - exports.MatchingBraceOutdent = MatchingBraceOutdent; - }); - - ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"], function(acequire, exports, module) { - "use strict"; - - var oop = acequire("../../lib/oop"); - var Behaviour = acequire("../behaviour").Behaviour; - var TokenIterator = acequire("../../token_iterator").TokenIterator; - var lang = acequire("../../lib/lang"); - - var SAFE_INSERT_IN_TOKENS = - ["text", "paren.rparen", "punctuation.operator"]; - var SAFE_INSERT_BEFORE_TOKENS = - ["text", "paren.rparen", "punctuation.operator", "comment"]; - - var context; - var contextCache = {}; - var initContext = function(editor) { - var id = -1; - if (editor.multiSelect) { - id = editor.selection.index; - if (contextCache.rangeCount != editor.multiSelect.rangeCount) - contextCache = {rangeCount: editor.multiSelect.rangeCount}; - } - if (contextCache[id]) - return context = contextCache[id]; - context = contextCache[id] = { - autoInsertedBrackets: 0, - autoInsertedRow: -1, - autoInsertedLineEnd: "", - maybeInsertedBrackets: 0, - maybeInsertedRow: -1, - maybeInsertedLineStart: "", - maybeInsertedLineEnd: "" - }; - }; - - var getWrapped = function(selection, selected, opening, closing) { - var rowDiff = selection.end.row - selection.start.row; - return { - text: opening + selected + closing, - selection: [ - 0, - selection.start.column + 1, - rowDiff, - selection.end.column + (rowDiff ? 0 : 1) - ] - }; - }; - - var CstyleBehaviour = function() { - this.add("braces", "insertion", function(state, action, editor, session, text) { - var cursor = editor.getCursorPosition(); - var line = session.doc.getLine(cursor.row); - if (text == '{') { - initContext(editor); - var selection = editor.getSelectionRange(); - var selected = session.doc.getTextRange(selection); - if (selected !== "" && selected !== "{" && editor.getWrapBehavioursEnabled()) { - return getWrapped(selection, selected, '{', '}'); - } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { - if (/[\]\}\)]/.test(line[cursor.column]) || editor.inMultiSelectMode) { - CstyleBehaviour.recordAutoInsert(editor, session, "}"); - return { - text: '{}', - selection: [1, 1] - }; - } else { - CstyleBehaviour.recordMaybeInsert(editor, session, "{"); - return { - text: '{', - selection: [1, 1] - }; - } - } - } else if (text == '}') { - initContext(editor); - var rightChar = line.substring(cursor.column, cursor.column + 1); - if (rightChar == '}') { - var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row}); - if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { - CstyleBehaviour.popAutoInsertedClosing(); - return { - text: '', - selection: [1, 1] - }; - } - } - } else if (text == "\n" || text == "\r\n") { - initContext(editor); - var closing = ""; - if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) { - closing = lang.stringRepeat("}", context.maybeInsertedBrackets); - CstyleBehaviour.clearMaybeInsertedClosing(); - } - var rightChar = line.substring(cursor.column, cursor.column + 1); - if (rightChar === '}') { - var openBracePos = session.findMatchingBracket({row: cursor.row, column: cursor.column+1}, '}'); - if (!openBracePos) - return null; - var next_indent = this.$getIndent(session.getLine(openBracePos.row)); - } else if (closing) { - var next_indent = this.$getIndent(line); - } else { - CstyleBehaviour.clearMaybeInsertedClosing(); - return; - } - var indent = next_indent + session.getTabString(); - - return { - text: '\n' + indent + '\n' + next_indent + closing, - selection: [1, indent.length, 1, indent.length] - }; - } else { - CstyleBehaviour.clearMaybeInsertedClosing(); - } - }); - - this.add("braces", "deletion", function(state, action, editor, session, range) { - var selected = session.doc.getTextRange(range); - if (!range.isMultiLine() && selected == '{') { - initContext(editor); - var line = session.doc.getLine(range.start.row); - var rightChar = line.substring(range.end.column, range.end.column + 1); - if (rightChar == '}') { - range.end.column++; - return range; - } else { - context.maybeInsertedBrackets--; - } - } - }); - - this.add("parens", "insertion", function(state, action, editor, session, text) { - if (text == '(') { - initContext(editor); - var selection = editor.getSelectionRange(); - var selected = session.doc.getTextRange(selection); - if (selected !== "" && editor.getWrapBehavioursEnabled()) { - return getWrapped(selection, selected, '(', ')'); - } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { - CstyleBehaviour.recordAutoInsert(editor, session, ")"); - return { - text: '()', - selection: [1, 1] - }; - } - } else if (text == ')') { - initContext(editor); - var cursor = editor.getCursorPosition(); - var line = session.doc.getLine(cursor.row); - var rightChar = line.substring(cursor.column, cursor.column + 1); - if (rightChar == ')') { - var matching = session.$findOpeningBracket(')', {column: cursor.column + 1, row: cursor.row}); - if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { - CstyleBehaviour.popAutoInsertedClosing(); - return { - text: '', - selection: [1, 1] - }; - } - } - } - }); - - this.add("parens", "deletion", function(state, action, editor, session, range) { - var selected = session.doc.getTextRange(range); - if (!range.isMultiLine() && selected == '(') { - initContext(editor); - var line = session.doc.getLine(range.start.row); - var rightChar = line.substring(range.start.column + 1, range.start.column + 2); - if (rightChar == ')') { - range.end.column++; - return range; - } - } - }); - - this.add("brackets", "insertion", function(state, action, editor, session, text) { - if (text == '[') { - initContext(editor); - var selection = editor.getSelectionRange(); - var selected = session.doc.getTextRange(selection); - if (selected !== "" && editor.getWrapBehavioursEnabled()) { - return getWrapped(selection, selected, '[', ']'); - } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { - CstyleBehaviour.recordAutoInsert(editor, session, "]"); - return { - text: '[]', - selection: [1, 1] - }; - } - } else if (text == ']') { - initContext(editor); - var cursor = editor.getCursorPosition(); - var line = session.doc.getLine(cursor.row); - var rightChar = line.substring(cursor.column, cursor.column + 1); - if (rightChar == ']') { - var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row}); - if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { - CstyleBehaviour.popAutoInsertedClosing(); - return { - text: '', - selection: [1, 1] - }; - } - } - } - }); - - this.add("brackets", "deletion", function(state, action, editor, session, range) { - var selected = session.doc.getTextRange(range); - if (!range.isMultiLine() && selected == '[') { - initContext(editor); - var line = session.doc.getLine(range.start.row); - var rightChar = line.substring(range.start.column + 1, range.start.column + 2); - if (rightChar == ']') { - range.end.column++; - return range; - } - } - }); - - this.add("string_dquotes", "insertion", function(state, action, editor, session, text) { - if (text == '"' || text == "'") { - initContext(editor); - var quote = text; - var selection = editor.getSelectionRange(); - var selected = session.doc.getTextRange(selection); - if (selected !== "" && selected !== "'" && selected != '"' && editor.getWrapBehavioursEnabled()) { - return getWrapped(selection, selected, quote, quote); - } else if (!selected) { - var cursor = editor.getCursorPosition(); - var line = session.doc.getLine(cursor.row); - var leftChar = line.substring(cursor.column-1, cursor.column); - var rightChar = line.substring(cursor.column, cursor.column + 1); - - var token = session.getTokenAt(cursor.row, cursor.column); - var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); - if (leftChar == "\\" && token && /escape/.test(token.type)) - return null; - - var stringBefore = token && /string|escape/.test(token.type); - var stringAfter = !rightToken || /string|escape/.test(rightToken.type); - - var pair; - if (rightChar == quote) { - pair = stringBefore !== stringAfter; - } else { - if (stringBefore && !stringAfter) - return null; // wrap string with different quote - if (stringBefore && stringAfter) - return null; // do not pair quotes inside strings - var wordRe = session.$mode.tokenRe; - wordRe.lastIndex = 0; - var isWordBefore = wordRe.test(leftChar); - wordRe.lastIndex = 0; - var isWordAfter = wordRe.test(leftChar); - if (isWordBefore || isWordAfter) - return null; // before or after alphanumeric - if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) - return null; // there is rightChar and it isn't closing - pair = true; - } - return { - text: pair ? quote + quote : "", - selection: [1,1] - }; - } - } - }); - - this.add("string_dquotes", "deletion", function(state, action, editor, session, range) { - var selected = session.doc.getTextRange(range); - if (!range.isMultiLine() && (selected == '"' || selected == "'")) { - initContext(editor); - var line = session.doc.getLine(range.start.row); - var rightChar = line.substring(range.start.column + 1, range.start.column + 2); - if (rightChar == selected) { - range.end.column++; - return range; - } - } - }); - - }; - - - CstyleBehaviour.isSaneInsertion = function(editor, session) { - var cursor = editor.getCursorPosition(); - var iterator = new TokenIterator(session, cursor.row, cursor.column); - if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) { - var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1); - if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) - return false; - } - iterator.stepForward(); - return iterator.getCurrentTokenRow() !== cursor.row || - this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS); - }; - - CstyleBehaviour.$matchTokenType = function(token, types) { - return types.indexOf(token.type || token) > -1; - }; - - CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) { - var cursor = editor.getCursorPosition(); - var line = session.doc.getLine(cursor.row); - if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0])) - context.autoInsertedBrackets = 0; - context.autoInsertedRow = cursor.row; - context.autoInsertedLineEnd = bracket + line.substr(cursor.column); - context.autoInsertedBrackets++; - }; - - CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) { - var cursor = editor.getCursorPosition(); - var line = session.doc.getLine(cursor.row); - if (!this.isMaybeInsertedClosing(cursor, line)) - context.maybeInsertedBrackets = 0; - context.maybeInsertedRow = cursor.row; - context.maybeInsertedLineStart = line.substr(0, cursor.column) + bracket; - context.maybeInsertedLineEnd = line.substr(cursor.column); - context.maybeInsertedBrackets++; - }; - - CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) { - return context.autoInsertedBrackets > 0 && - cursor.row === context.autoInsertedRow && - bracket === context.autoInsertedLineEnd[0] && - line.substr(cursor.column) === context.autoInsertedLineEnd; - }; - - CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) { - return context.maybeInsertedBrackets > 0 && - cursor.row === context.maybeInsertedRow && - line.substr(cursor.column) === context.maybeInsertedLineEnd && - line.substr(0, cursor.column) == context.maybeInsertedLineStart; - }; - - CstyleBehaviour.popAutoInsertedClosing = function() { - context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); - context.autoInsertedBrackets--; - }; - - CstyleBehaviour.clearMaybeInsertedClosing = function() { - if (context) { - context.maybeInsertedBrackets = 0; - context.maybeInsertedRow = -1; - } - }; - - - - oop.inherits(CstyleBehaviour, Behaviour); - - exports.CstyleBehaviour = CstyleBehaviour; - }); - - ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(acequire, exports, module) { - "use strict"; - - var oop = acequire("../../lib/oop"); - var Range = acequire("../../range").Range; - var BaseFoldMode = acequire("./fold_mode").FoldMode; - - var FoldMode = exports.FoldMode = function(commentRegex) { - if (commentRegex) { - this.foldingStartMarker = new RegExp( - this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start) - ); - this.foldingStopMarker = new RegExp( - this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end) - ); - } - }; - oop.inherits(FoldMode, BaseFoldMode); - - (function() { - - this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/; - this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/; - this.singleLineBlockCommentRe= /^\s*(\/\*).*\*\/\s*$/; - this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/; - this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/; - this._getFoldWidgetBase = this.getFoldWidget; - this.getFoldWidget = function(session, foldStyle, row) { - var line = session.getLine(row); - - if (this.singleLineBlockCommentRe.test(line)) { - if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line)) - return ""; - } - - var fw = this._getFoldWidgetBase(session, foldStyle, row); - - if (!fw && this.startRegionRe.test(line)) - return "start"; // lineCommentRegionStart - - return fw; - }; - - this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) { - var line = session.getLine(row); - - if (this.startRegionRe.test(line)) - return this.getCommentRegionBlock(session, line, row); - - var match = line.match(this.foldingStartMarker); - if (match) { - var i = match.index; - - if (match[1]) - return this.openingBracketBlock(session, match[1], row, i); - - var range = session.getCommentFoldRange(row, i + match[0].length, 1); - - if (range && !range.isMultiLine()) { - if (forceMultiline) { - range = this.getSectionRange(session, row); - } else if (foldStyle != "all") - range = null; - } - - return range; - } - - if (foldStyle === "markbegin") - return; - - var match = line.match(this.foldingStopMarker); - if (match) { - var i = match.index + match[0].length; - - if (match[1]) - return this.closingBracketBlock(session, match[1], row, i); - - return session.getCommentFoldRange(row, i, -1); - } - }; - - this.getSectionRange = function(session, row) { - var line = session.getLine(row); - var startIndent = line.search(/\S/); - var startRow = row; - var startColumn = line.length; - row = row + 1; - var endRow = row; - var maxRow = session.getLength(); - while (++row < maxRow) { - line = session.getLine(row); - var indent = line.search(/\S/); - if (indent === -1) - continue; - if (startIndent > indent) - break; - var subRange = this.getFoldWidgetRange(session, "all", row); - - if (subRange) { - if (subRange.start.row <= startRow) { - break; - } else if (subRange.isMultiLine()) { - row = subRange.end.row; - } else if (startIndent == indent) { - break; - } - } - endRow = row; - } - - return new Range(startRow, startColumn, endRow, session.getLine(endRow).length); - }; - this.getCommentRegionBlock = function(session, line, row) { - var startColumn = line.search(/\s*$/); - var maxRow = session.getLength(); - var startRow = row; - - var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/; - var depth = 1; - while (++row < maxRow) { - line = session.getLine(row); - var m = re.exec(line); - if (!m) continue; - if (m[1]) depth--; - else depth++; - - if (!depth) break; - } - - var endRow = row; - if (endRow > startRow) { - return new Range(startRow, startColumn, endRow, line.length); - } - }; - - }).call(FoldMode.prototype); - - }); - - ace.define("ace/mode/json",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/worker/worker_client"], function(acequire, exports, module) { - "use strict"; - - var oop = acequire("../lib/oop"); - var TextMode = acequire("./text").Mode; - var HighlightRules = acequire("./json_highlight_rules").JsonHighlightRules; - var MatchingBraceOutdent = acequire("./matching_brace_outdent").MatchingBraceOutdent; - var CstyleBehaviour = acequire("./behaviour/cstyle").CstyleBehaviour; - var CStyleFoldMode = acequire("./folding/cstyle").FoldMode; - var WorkerClient = acequire("../worker/worker_client").WorkerClient; - - var Mode = function() { - this.HighlightRules = HighlightRules; - this.$outdent = new MatchingBraceOutdent(); - this.$behaviour = new CstyleBehaviour(); - this.foldingRules = new CStyleFoldMode(); - }; - oop.inherits(Mode, TextMode); - - (function() { - - this.getNextLineIndent = function(state, line, tab) { - var indent = this.$getIndent(line); - - if (state == "start") { - var match = line.match(/^.*[\{\(\[]\s*$/); - if (match) { - indent += tab; - } - } - - return indent; - }; - - this.checkOutdent = function(state, line, input) { - return this.$outdent.checkOutdent(line, input); - }; - - this.autoOutdent = function(state, doc, row) { - this.$outdent.autoOutdent(doc, row); - }; - - this.createWorker = function(session) { - var worker = new WorkerClient(["ace"], __webpack_require__(15), "JsonWorker"); - worker.attachToDocument(session.getDocument()); - - worker.on("annotate", function(e) { - session.setAnnotations(e.data); - }); - - worker.on("terminate", function() { - session.clearAnnotations(); - }); - - return worker; - }; - - - this.$id = "ace/mode/json"; - }).call(Mode.prototype); - - exports.Mode = Mode; - }); - - -/***/ }, -/* 15 */ -/***/ function(module, exports) { - - module.exports.id = 'ace/mode/json_worker'; - module.exports.src = "\"no use strict\";(function(window){function resolveModuleId(id,paths){for(var testPath=id,tail=\"\";testPath;){var alias=paths[testPath];if(\"string\"==typeof alias)return alias+tail;if(alias)return alias.location.replace(/\\/*$/,\"/\")+(tail||alias.main||alias.name);if(alias===!1)return\"\";var i=testPath.lastIndexOf(\"/\");if(-1===i)break;tail=testPath.substr(i)+tail,testPath=testPath.slice(0,i)}return id}if(!(void 0!==window.window&&window.document||window.acequire&&window.define)){window.console||(window.console=function(){var msgs=Array.prototype.slice.call(arguments,0);postMessage({type:\"log\",data:msgs})},window.console.error=window.console.warn=window.console.log=window.console.trace=window.console),window.window=window,window.ace=window,window.onerror=function(message,file,line,col,err){postMessage({type:\"error\",data:{message:message,data:err.data,file:file,line:line,col:col,stack:err.stack}})},window.normalizeModule=function(parentId,moduleName){if(-1!==moduleName.indexOf(\"!\")){var chunks=moduleName.split(\"!\");return window.normalizeModule(parentId,chunks[0])+\"!\"+window.normalizeModule(parentId,chunks[1])}if(\".\"==moduleName.charAt(0)){var base=parentId.split(\"/\").slice(0,-1).join(\"/\");for(moduleName=(base?base+\"/\":\"\")+moduleName;-1!==moduleName.indexOf(\".\")&&previous!=moduleName;){var previous=moduleName;moduleName=moduleName.replace(/^\\.\\//,\"\").replace(/\\/\\.\\//,\"/\").replace(/[^\\/]+\\/\\.\\.\\//,\"\")}}return moduleName},window.acequire=function acequire(parentId,id){if(id||(id=parentId,parentId=null),!id.charAt)throw Error(\"worker.js acequire() accepts only (parentId, id) as arguments\");id=window.normalizeModule(parentId,id);var module=window.acequire.modules[id];if(module)return module.initialized||(module.initialized=!0,module.exports=module.factory().exports),module.exports;if(!window.acequire.tlns)return console.log(\"unable to load \"+id);var path=resolveModuleId(id,window.acequire.tlns);return\".js\"!=path.slice(-3)&&(path+=\".js\"),window.acequire.id=id,window.acequire.modules[id]={},importScripts(path),window.acequire(parentId,id)},window.acequire.modules={},window.acequire.tlns={},window.define=function(id,deps,factory){if(2==arguments.length?(factory=deps,\"string\"!=typeof id&&(deps=id,id=window.acequire.id)):1==arguments.length&&(factory=id,deps=[],id=window.acequire.id),\"function\"!=typeof factory)return window.acequire.modules[id]={exports:factory,initialized:!0},void 0;deps.length||(deps=[\"require\",\"exports\",\"module\"]);var req=function(childId){return window.acequire(id,childId)};window.acequire.modules[id]={exports:{},factory:function(){var module=this,returnExports=factory.apply(this,deps.map(function(dep){switch(dep){case\"require\":return req;case\"exports\":return module.exports;case\"module\":return module;default:return req(dep)}}));return returnExports&&(module.exports=returnExports),module}}},window.define.amd={},acequire.tlns={},window.initBaseUrls=function(topLevelNamespaces){for(var i in topLevelNamespaces)acequire.tlns[i]=topLevelNamespaces[i]},window.initSender=function(){var EventEmitter=window.acequire(\"ace/lib/event_emitter\").EventEmitter,oop=window.acequire(\"ace/lib/oop\"),Sender=function(){};return function(){oop.implement(this,EventEmitter),this.callback=function(data,callbackId){postMessage({type:\"call\",id:callbackId,data:data})},this.emit=function(name,data){postMessage({type:\"event\",name:name,data:data})}}.call(Sender.prototype),new Sender};var main=window.main=null,sender=window.sender=null;window.onmessage=function(e){var msg=e.data;if(msg.event&&sender)sender._signal(msg.event,msg.data);else if(msg.command)if(main[msg.command])main[msg.command].apply(main,msg.args);else{if(!window[msg.command])throw Error(\"Unknown command:\"+msg.command);window[msg.command].apply(window,msg.args)}else if(msg.init){window.initBaseUrls(msg.tlns),acequire(\"ace/lib/es5-shim\"),sender=window.sender=window.initSender();var clazz=acequire(msg.module)[msg.classname];main=window.main=new clazz(sender)}}}})(this),ace.define(\"ace/lib/oop\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";exports.inherits=function(ctor,superCtor){ctor.super_=superCtor,ctor.prototype=Object.create(superCtor.prototype,{constructor:{value:ctor,enumerable:!1,writable:!0,configurable:!0}})},exports.mixin=function(obj,mixin){for(var key in mixin)obj[key]=mixin[key];return obj},exports.implement=function(proto,mixin){exports.mixin(proto,mixin)}}),ace.define(\"ace/range\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";var comparePoints=function(p1,p2){return p1.row-p2.row||p1.column-p2.column},Range=function(startRow,startColumn,endRow,endColumn){this.start={row:startRow,column:startColumn},this.end={row:endRow,column:endColumn}};(function(){this.isEqual=function(range){return this.start.row===range.start.row&&this.end.row===range.end.row&&this.start.column===range.start.column&&this.end.column===range.end.column},this.toString=function(){return\"Range: [\"+this.start.row+\"/\"+this.start.column+\"] -> [\"+this.end.row+\"/\"+this.end.column+\"]\"},this.contains=function(row,column){return 0==this.compare(row,column)},this.compareRange=function(range){var cmp,end=range.end,start=range.start;return cmp=this.compare(end.row,end.column),1==cmp?(cmp=this.compare(start.row,start.column),1==cmp?2:0==cmp?1:0):-1==cmp?-2:(cmp=this.compare(start.row,start.column),-1==cmp?-1:1==cmp?42:0)},this.comparePoint=function(p){return this.compare(p.row,p.column)},this.containsRange=function(range){return 0==this.comparePoint(range.start)&&0==this.comparePoint(range.end)},this.intersects=function(range){var cmp=this.compareRange(range);return-1==cmp||0==cmp||1==cmp},this.isEnd=function(row,column){return this.end.row==row&&this.end.column==column},this.isStart=function(row,column){return this.start.row==row&&this.start.column==column},this.setStart=function(row,column){\"object\"==typeof row?(this.start.column=row.column,this.start.row=row.row):(this.start.row=row,this.start.column=column)},this.setEnd=function(row,column){\"object\"==typeof row?(this.end.column=row.column,this.end.row=row.row):(this.end.row=row,this.end.column=column)},this.inside=function(row,column){return 0==this.compare(row,column)?this.isEnd(row,column)||this.isStart(row,column)?!1:!0:!1},this.insideStart=function(row,column){return 0==this.compare(row,column)?this.isEnd(row,column)?!1:!0:!1},this.insideEnd=function(row,column){return 0==this.compare(row,column)?this.isStart(row,column)?!1:!0:!1},this.compare=function(row,column){return this.isMultiLine()||row!==this.start.row?this.start.row>row?-1:row>this.end.row?1:this.start.row===row?column>=this.start.column?0:-1:this.end.row===row?this.end.column>=column?0:1:0:this.start.column>column?-1:column>this.end.column?1:0},this.compareStart=function(row,column){return this.start.row==row&&this.start.column==column?-1:this.compare(row,column)},this.compareEnd=function(row,column){return this.end.row==row&&this.end.column==column?1:this.compare(row,column)},this.compareInside=function(row,column){return this.end.row==row&&this.end.column==column?1:this.start.row==row&&this.start.column==column?-1:this.compare(row,column)},this.clipRows=function(firstRow,lastRow){if(this.end.row>lastRow)var end={row:lastRow+1,column:0};else if(firstRow>this.end.row)var end={row:firstRow,column:0};if(this.start.row>lastRow)var start={row:lastRow+1,column:0};else if(firstRow>this.start.row)var start={row:firstRow,column:0};return Range.fromPoints(start||this.start,end||this.end)},this.extend=function(row,column){var cmp=this.compare(row,column);if(0==cmp)return this;if(-1==cmp)var start={row:row,column:column};else var end={row:row,column:column};return Range.fromPoints(start||this.start,end||this.end)},this.isEmpty=function(){return this.start.row===this.end.row&&this.start.column===this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return Range.fromPoints(this.start,this.end)},this.collapseRows=function(){return 0==this.end.column?new Range(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new Range(this.start.row,0,this.end.row,0)},this.toScreenRange=function(session){var screenPosStart=session.documentToScreenPosition(this.start),screenPosEnd=session.documentToScreenPosition(this.end);return new Range(screenPosStart.row,screenPosStart.column,screenPosEnd.row,screenPosEnd.column)},this.moveBy=function(row,column){this.start.row+=row,this.start.column+=column,this.end.row+=row,this.end.column+=column}}).call(Range.prototype),Range.fromPoints=function(start,end){return new Range(start.row,start.column,end.row,end.column)},Range.comparePoints=comparePoints,Range.comparePoints=function(p1,p2){return p1.row-p2.row||p1.column-p2.column},exports.Range=Range}),ace.define(\"ace/apply_delta\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";exports.applyDelta=function(docLines,delta){var row=delta.start.row,startColumn=delta.start.column,line=docLines[row]||\"\";switch(delta.action){case\"insert\":var lines=delta.lines;if(1===lines.length)docLines[row]=line.substring(0,startColumn)+delta.lines[0]+line.substring(startColumn);else{var args=[row,1].concat(delta.lines);docLines.splice.apply(docLines,args),docLines[row]=line.substring(0,startColumn)+docLines[row],docLines[row+delta.lines.length-1]+=line.substring(startColumn)}break;case\"remove\":var endColumn=delta.end.column,endRow=delta.end.row;row===endRow?docLines[row]=line.substring(0,startColumn)+line.substring(endColumn):docLines.splice(row,endRow-row+1,line.substring(0,startColumn)+docLines[endRow].substring(endColumn))}}}),ace.define(\"ace/lib/event_emitter\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";var EventEmitter={},stopPropagation=function(){this.propagationStopped=!0},preventDefault=function(){this.defaultPrevented=!0};EventEmitter._emit=EventEmitter._dispatchEvent=function(eventName,e){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var listeners=this._eventRegistry[eventName]||[],defaultHandler=this._defaultHandlers[eventName];if(listeners.length||defaultHandler){\"object\"==typeof e&&e||(e={}),e.type||(e.type=eventName),e.stopPropagation||(e.stopPropagation=stopPropagation),e.preventDefault||(e.preventDefault=preventDefault),listeners=listeners.slice();for(var i=0;listeners.length>i&&(listeners[i](e,this),!e.propagationStopped);i++);return defaultHandler&&!e.defaultPrevented?defaultHandler(e,this):void 0}},EventEmitter._signal=function(eventName,e){var listeners=(this._eventRegistry||{})[eventName];if(listeners){listeners=listeners.slice();for(var i=0;listeners.length>i;i++)listeners[i](e,this)}},EventEmitter.once=function(eventName,callback){var _self=this;callback&&this.addEventListener(eventName,function newCallback(){_self.removeEventListener(eventName,newCallback),callback.apply(null,arguments)})},EventEmitter.setDefaultHandler=function(eventName,callback){var handlers=this._defaultHandlers;if(handlers||(handlers=this._defaultHandlers={_disabled_:{}}),handlers[eventName]){var old=handlers[eventName],disabled=handlers._disabled_[eventName];disabled||(handlers._disabled_[eventName]=disabled=[]),disabled.push(old);var i=disabled.indexOf(callback);-1!=i&&disabled.splice(i,1)}handlers[eventName]=callback},EventEmitter.removeDefaultHandler=function(eventName,callback){var handlers=this._defaultHandlers;if(handlers){var disabled=handlers._disabled_[eventName];if(handlers[eventName]==callback)handlers[eventName],disabled&&this.setDefaultHandler(eventName,disabled.pop());else if(disabled){var i=disabled.indexOf(callback);-1!=i&&disabled.splice(i,1)}}},EventEmitter.on=EventEmitter.addEventListener=function(eventName,callback,capturing){this._eventRegistry=this._eventRegistry||{};var listeners=this._eventRegistry[eventName];return listeners||(listeners=this._eventRegistry[eventName]=[]),-1==listeners.indexOf(callback)&&listeners[capturing?\"unshift\":\"push\"](callback),callback},EventEmitter.off=EventEmitter.removeListener=EventEmitter.removeEventListener=function(eventName,callback){this._eventRegistry=this._eventRegistry||{};var listeners=this._eventRegistry[eventName];if(listeners){var index=listeners.indexOf(callback);-1!==index&&listeners.splice(index,1)}},EventEmitter.removeAllListeners=function(eventName){this._eventRegistry&&(this._eventRegistry[eventName]=[])},exports.EventEmitter=EventEmitter}),ace.define(\"ace/anchor\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/event_emitter\"],function(acequire,exports){\"use strict\";var oop=acequire(\"./lib/oop\"),EventEmitter=acequire(\"./lib/event_emitter\").EventEmitter,Anchor=exports.Anchor=function(doc,row,column){this.$onChange=this.onChange.bind(this),this.attach(doc),column===void 0?this.setPosition(row.row,row.column):this.setPosition(row,column)};(function(){function $pointsInOrder(point1,point2,equalPointsInOrder){var bColIsAfter=equalPointsInOrder?point1.column<=point2.column:point1.columnthis.row)){var point=$getTransformedPoint(delta,{row:this.row,column:this.column},this.$insertRight);this.setPosition(point.row,point.column,!0)}},this.setPosition=function(row,column,noClip){var pos;if(pos=noClip?{row:row,column:column}:this.$clipPositionToDocument(row,column),this.row!=pos.row||this.column!=pos.column){var old={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._signal(\"change\",{old:old,value:pos})}},this.detach=function(){this.document.removeEventListener(\"change\",this.$onChange)},this.attach=function(doc){this.document=doc||this.document,this.document.on(\"change\",this.$onChange)},this.$clipPositionToDocument=function(row,column){var pos={};return row>=this.document.getLength()?(pos.row=Math.max(0,this.document.getLength()-1),pos.column=this.document.getLine(pos.row).length):0>row?(pos.row=0,pos.column=0):(pos.row=row,pos.column=Math.min(this.document.getLine(pos.row).length,Math.max(0,column))),0>column&&(pos.column=0),pos}}).call(Anchor.prototype)}),ace.define(\"ace/document\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/apply_delta\",\"ace/lib/event_emitter\",\"ace/range\",\"ace/anchor\"],function(acequire,exports){\"use strict\";var oop=acequire(\"./lib/oop\"),applyDelta=acequire(\"./apply_delta\").applyDelta,EventEmitter=acequire(\"./lib/event_emitter\").EventEmitter,Range=acequire(\"./range\").Range,Anchor=acequire(\"./anchor\").Anchor,Document=function(textOrLines){this.$lines=[\"\"],0===textOrLines.length?this.$lines=[\"\"]:Array.isArray(textOrLines)?this.insertMergedLines({row:0,column:0},textOrLines):this.insert({row:0,column:0},textOrLines)};(function(){oop.implement(this,EventEmitter),this.setValue=function(text){var len=this.getLength()-1;this.remove(new Range(0,0,len,this.getLine(len).length)),this.insert({row:0,column:0},text)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(row,column){return new Anchor(this,row,column)},this.$split=0===\"aaa\".split(/a/).length?function(text){return text.replace(/\\r\\n|\\r/g,\"\\n\").split(\"\\n\")}:function(text){return text.split(/\\r\\n|\\r|\\n/)},this.$detectNewLine=function(text){var match=text.match(/^.*?(\\r\\n|\\r|\\n)/m);this.$autoNewLine=match?match[1]:\"\\n\",this._signal(\"changeNewLineMode\")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case\"windows\":return\"\\r\\n\";case\"unix\":return\"\\n\";default:return this.$autoNewLine||\"\\n\"}},this.$autoNewLine=\"\",this.$newLineMode=\"auto\",this.setNewLineMode=function(newLineMode){this.$newLineMode!==newLineMode&&(this.$newLineMode=newLineMode,this._signal(\"changeNewLineMode\"))},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(text){return\"\\r\\n\"==text||\"\\r\"==text||\"\\n\"==text},this.getLine=function(row){return this.$lines[row]||\"\"},this.getLines=function(firstRow,lastRow){return this.$lines.slice(firstRow,lastRow+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(range){return this.getLinesForRange(range).join(this.getNewLineCharacter())},this.getLinesForRange=function(range){var lines;if(range.start.row===range.end.row)lines=[this.getLine(range.start.row).substring(range.start.column,range.end.column)];else{lines=this.getLines(range.start.row,range.end.row),lines[0]=(lines[0]||\"\").substring(range.start.column);var l=lines.length-1;range.end.row-range.start.row==l&&(lines[l]=lines[l].substring(0,range.end.column))}return lines},this.insertLines=function(row,lines){return console.warn(\"Use of document.insertLines is deprecated. Use the insertFullLines method instead.\"),this.insertFullLines(row,lines)},this.removeLines=function(firstRow,lastRow){return console.warn(\"Use of document.removeLines is deprecated. Use the removeFullLines method instead.\"),this.removeFullLines(firstRow,lastRow)},this.insertNewLine=function(position){return console.warn(\"Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead.\"),this.insertMergedLines(position,[\"\",\"\"])},this.insert=function(position,text){return 1>=this.getLength()&&this.$detectNewLine(text),this.insertMergedLines(position,this.$split(text))},this.insertInLine=function(position,text){var start=this.clippedPos(position.row,position.column),end=this.pos(position.row,position.column+text.length);return this.applyDelta({start:start,end:end,action:\"insert\",lines:[text]},!0),this.clonePos(end)},this.clippedPos=function(row,column){var length=this.getLength();void 0===row?row=length:0>row?row=0:row>=length&&(row=length-1,column=void 0);var line=this.getLine(row);return void 0==column&&(column=line.length),column=Math.min(Math.max(column,0),line.length),{row:row,column:column}},this.clonePos=function(pos){return{row:pos.row,column:pos.column}},this.pos=function(row,column){return{row:row,column:column}},this.$clipPosition=function(position){var length=this.getLength();return position.row>=length?(position.row=Math.max(0,length-1),position.column=this.getLine(length-1).length):(position.row=Math.max(0,position.row),position.column=Math.min(Math.max(position.column,0),this.getLine(position.row).length)),position},this.insertFullLines=function(row,lines){row=Math.min(Math.max(row,0),this.getLength());var column=0;this.getLength()>row?(lines=lines.concat([\"\"]),column=0):(lines=[\"\"].concat(lines),row--,column=this.$lines[row].length),this.insertMergedLines({row:row,column:column},lines)},this.insertMergedLines=function(position,lines){var start=this.clippedPos(position.row,position.column),end={row:start.row+lines.length-1,column:(1==lines.length?start.column:0)+lines[lines.length-1].length};return this.applyDelta({start:start,end:end,action:\"insert\",lines:lines}),this.clonePos(end)},this.remove=function(range){var start=this.clippedPos(range.start.row,range.start.column),end=this.clippedPos(range.end.row,range.end.column);return this.applyDelta({start:start,end:end,action:\"remove\",lines:this.getLinesForRange({start:start,end:end})}),this.clonePos(start)},this.removeInLine=function(row,startColumn,endColumn){var start=this.clippedPos(row,startColumn),end=this.clippedPos(row,endColumn);return this.applyDelta({start:start,end:end,action:\"remove\",lines:this.getLinesForRange({start:start,end:end})},!0),this.clonePos(start)},this.removeFullLines=function(firstRow,lastRow){firstRow=Math.min(Math.max(0,firstRow),this.getLength()-1),lastRow=Math.min(Math.max(0,lastRow),this.getLength()-1);var deleteFirstNewLine=lastRow==this.getLength()-1&&firstRow>0,deleteLastNewLine=this.getLength()-1>lastRow,startRow=deleteFirstNewLine?firstRow-1:firstRow,startCol=deleteFirstNewLine?this.getLine(startRow).length:0,endRow=deleteLastNewLine?lastRow+1:lastRow,endCol=deleteLastNewLine?0:this.getLine(endRow).length,range=new Range(startRow,startCol,endRow,endCol),deletedLines=this.$lines.slice(firstRow,lastRow+1);return this.applyDelta({start:range.start,end:range.end,action:\"remove\",lines:this.getLinesForRange(range)}),deletedLines},this.removeNewLine=function(row){this.getLength()-1>row&&row>=0&&this.applyDelta({start:this.pos(row,this.getLine(row).length),end:this.pos(row+1,0),action:\"remove\",lines:[\"\",\"\"]})},this.replace=function(range,text){if(range instanceof Range||(range=Range.fromPoints(range.start,range.end)),0===text.length&&range.isEmpty())return range.start;if(text==this.getTextRange(range))return range.end;this.remove(range);var end;return end=text?this.insert(range.start,text):range.start},this.applyDeltas=function(deltas){for(var i=0;deltas.length>i;i++)this.applyDelta(deltas[i])},this.revertDeltas=function(deltas){for(var i=deltas.length-1;i>=0;i--)this.revertDelta(deltas[i])},this.applyDelta=function(delta,doNotValidate){var isInsert=\"insert\"==delta.action;(isInsert?1>=delta.lines.length&&!delta.lines[0]:!Range.comparePoints(delta.start,delta.end))||(isInsert&&delta.lines.length>2e4&&this.$splitAndapplyLargeDelta(delta,2e4),applyDelta(this.$lines,delta,doNotValidate),this._signal(\"change\",delta))},this.$splitAndapplyLargeDelta=function(delta,MAX){for(var lines=delta.lines,l=lines.length,row=delta.start.row,column=delta.start.column,from=0,to=0;;){from=to,to+=MAX-1;var chunk=lines.slice(from,to);if(to>l){delta.lines=chunk,delta.start.row=row+from,delta.start.column=column;break}chunk.push(\"\"),this.applyDelta({start:this.pos(row+from,column),end:this.pos(row+to,column=0),action:delta.action,lines:chunk},!0)}},this.revertDelta=function(delta){this.applyDelta({start:this.clonePos(delta.start),end:this.clonePos(delta.end),action:\"insert\"==delta.action?\"remove\":\"insert\",lines:delta.lines.slice()})},this.indexToPosition=function(index,startRow){for(var lines=this.$lines||this.getAllLines(),newlineLength=this.getNewLineCharacter().length,i=startRow||0,l=lines.length;l>i;i++)if(index-=lines[i].length+newlineLength,0>index)return{row:i,column:index+lines[i].length+newlineLength};return{row:l-1,column:lines[l-1].length}},this.positionToIndex=function(pos,startRow){for(var lines=this.$lines||this.getAllLines(),newlineLength=this.getNewLineCharacter().length,index=0,row=Math.min(pos.row,lines.length),i=startRow||0;row>i;++i)index+=lines[i].length+newlineLength;return index+pos.column}}).call(Document.prototype),exports.Document=Document}),ace.define(\"ace/lib/lang\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";exports.last=function(a){return a[a.length-1]},exports.stringReverse=function(string){return string.split(\"\").reverse().join(\"\")},exports.stringRepeat=function(string,count){for(var result=\"\";count>0;)1&count&&(result+=string),(count>>=1)&&(string+=string);return result};var trimBeginRegexp=/^\\s\\s*/,trimEndRegexp=/\\s\\s*$/;exports.stringTrimLeft=function(string){return string.replace(trimBeginRegexp,\"\")},exports.stringTrimRight=function(string){return string.replace(trimEndRegexp,\"\")},exports.copyObject=function(obj){var copy={};for(var key in obj)copy[key]=obj[key];return copy},exports.copyArray=function(array){for(var copy=[],i=0,l=array.length;l>i;i++)copy[i]=array[i]&&\"object\"==typeof array[i]?this.copyObject(array[i]):array[i];return copy},exports.deepCopy=function deepCopy(obj){if(\"object\"!=typeof obj||!obj)return obj;var copy;if(Array.isArray(obj)){copy=[];for(var key=0;obj.length>key;key++)copy[key]=deepCopy(obj[key]);return copy}var cons=obj.constructor;if(cons===RegExp)return obj;copy=cons();for(var key in obj)copy[key]=deepCopy(obj[key]);return copy},exports.arrayToMap=function(arr){for(var map={},i=0;arr.length>i;i++)map[arr[i]]=1;return map},exports.createMap=function(props){var map=Object.create(null);for(var i in props)map[i]=props[i];return map},exports.arrayRemove=function(array,value){for(var i=0;array.length>=i;i++)value===array[i]&&array.splice(i,1)},exports.escapeRegExp=function(str){return str.replace(/([.*+?^${}()|[\\]\\/\\\\])/g,\"\\\\$1\")},exports.escapeHTML=function(str){return str.replace(/&/g,\"&\").replace(/\"/g,\""\").replace(/'/g,\"'\").replace(/i;i+=2){if(Array.isArray(data[i+1]))var d={action:\"insert\",start:data[i],lines:data[i+1]};else var d={action:\"remove\",start:data[i],end:data[i+1]};doc.applyDelta(d,!0)}return _self.$timeout?deferredUpdate.schedule(_self.$timeout):(_self.onUpdate(),void 0)})};(function(){this.$timeout=500,this.setTimeout=function(timeout){this.$timeout=timeout},this.setValue=function(value){this.doc.setValue(value),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(callbackId){this.sender.callback(this.doc.getValue(),callbackId)},this.onUpdate=function(){},this.isPending=function(){return this.deferredUpdate.isPending()}}).call(Mirror.prototype)}),ace.define(\"ace/mode/json/json_parse\",[\"require\",\"exports\",\"module\"],function(){\"use strict\";var at,ch,text,value,escapee={'\"':'\"',\"\\\\\":\"\\\\\",\"/\":\"/\",b:\"\\b\",f:\"\\f\",n:\"\\n\",r:\"\\r\",t:\"\t\"},error=function(m){throw{name:\"SyntaxError\",message:m,at:at,text:text}},next=function(c){return c&&c!==ch&&error(\"Expected '\"+c+\"' instead of '\"+ch+\"'\"),ch=text.charAt(at),at+=1,ch},number=function(){var number,string=\"\";for(\"-\"===ch&&(string=\"-\",next(\"-\"));ch>=\"0\"&&\"9\">=ch;)string+=ch,next();if(\".\"===ch)for(string+=\".\";next()&&ch>=\"0\"&&\"9\">=ch;)string+=ch;if(\"e\"===ch||\"E\"===ch)for(string+=ch,next(),(\"-\"===ch||\"+\"===ch)&&(string+=ch,next());ch>=\"0\"&&\"9\">=ch;)string+=ch,next();return number=+string,isNaN(number)?(error(\"Bad number\"),void 0):number},string=function(){var hex,i,uffff,string=\"\";if('\"'===ch)for(;next();){if('\"'===ch)return next(),string;if(\"\\\\\"===ch)if(next(),\"u\"===ch){for(uffff=0,i=0;4>i&&(hex=parseInt(next(),16),isFinite(hex));i+=1)uffff=16*uffff+hex;string+=String.fromCharCode(uffff)}else{if(\"string\"!=typeof escapee[ch])break;string+=escapee[ch]}else string+=ch}error(\"Bad string\")},white=function(){for(;ch&&\" \">=ch;)next()},word=function(){switch(ch){case\"t\":return next(\"t\"),next(\"r\"),next(\"u\"),next(\"e\"),!0;case\"f\":return next(\"f\"),next(\"a\"),next(\"l\"),next(\"s\"),next(\"e\"),!1;case\"n\":return next(\"n\"),next(\"u\"),next(\"l\"),next(\"l\"),null}error(\"Unexpected '\"+ch+\"'\")},array=function(){var array=[];if(\"[\"===ch){if(next(\"[\"),white(),\"]\"===ch)return next(\"]\"),array;for(;ch;){if(array.push(value()),white(),\"]\"===ch)return next(\"]\"),array;next(\",\"),white()}}error(\"Bad array\")},object=function(){var key,object={};if(\"{\"===ch){if(next(\"{\"),white(),\"}\"===ch)return next(\"}\"),object;for(;ch;){if(key=string(),white(),next(\":\"),Object.hasOwnProperty.call(object,key)&&error('Duplicate key \"'+key+'\"'),object[key]=value(),white(),\"}\"===ch)return next(\"}\"),object;next(\",\"),white()}}error(\"Bad object\")};return value=function(){switch(white(),ch){case\"{\":return object();case\"[\":return array();case'\"':return string();case\"-\":return number();default:return ch>=\"0\"&&\"9\">=ch?number():word()}},function(source,reviver){var result;return text=source,at=0,ch=\" \",result=value(),white(),ch&&error(\"Syntax error\"),\"function\"==typeof reviver?function walk(holder,key){var k,v,value=holder[key];if(value&&\"object\"==typeof value)for(k in value)Object.hasOwnProperty.call(value,k)&&(v=walk(value,k),void 0!==v?value[k]=v:delete value[k]);return reviver.call(holder,key,value)}({\"\":result},\"\"):result}}),ace.define(\"ace/mode/json_worker\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/worker/mirror\",\"ace/mode/json/json_parse\"],function(acequire,exports){\"use strict\";var oop=acequire(\"../lib/oop\"),Mirror=acequire(\"../worker/mirror\").Mirror,parse=acequire(\"./json/json_parse\"),JsonWorker=exports.JsonWorker=function(sender){Mirror.call(this,sender),this.setTimeout(200)};oop.inherits(JsonWorker,Mirror),function(){this.onUpdate=function(){var value=this.doc.getValue(),errors=[];try{value&&parse(value)}catch(e){var pos=this.doc.indexToPosition(e.at-1);errors.push({row:pos.row,column:pos.column,text:e.message,type:\"error\"})}this.sender.emit(\"annotate\",errors)}}.call(JsonWorker.prototype)}),ace.define(\"ace/lib/es5-shim\",[\"require\",\"exports\",\"module\"],function(){function Empty(){}function doesDefinePropertyWork(object){try{return Object.defineProperty(object,\"sentinel\",{}),\"sentinel\"in object}catch(exception){}}function toInteger(n){return n=+n,n!==n?n=0:0!==n&&n!==1/0&&n!==-(1/0)&&(n=(n>0||-1)*Math.floor(Math.abs(n))),n}Function.prototype.bind||(Function.prototype.bind=function(that){var target=this;if(\"function\"!=typeof target)throw new TypeError(\"Function.prototype.bind called on incompatible \"+target);var args=slice.call(arguments,1),bound=function(){if(this instanceof bound){var result=target.apply(this,args.concat(slice.call(arguments)));return Object(result)===result?result:this}return target.apply(that,args.concat(slice.call(arguments)))};return target.prototype&&(Empty.prototype=target.prototype,bound.prototype=new Empty,Empty.prototype=null),bound});var defineGetter,defineSetter,lookupGetter,lookupSetter,supportsAccessors,call=Function.prototype.call,prototypeOfArray=Array.prototype,prototypeOfObject=Object.prototype,slice=prototypeOfArray.slice,_toString=call.bind(prototypeOfObject.toString),owns=call.bind(prototypeOfObject.hasOwnProperty);if((supportsAccessors=owns(prototypeOfObject,\"__defineGetter__\"))&&(defineGetter=call.bind(prototypeOfObject.__defineGetter__),defineSetter=call.bind(prototypeOfObject.__defineSetter__),lookupGetter=call.bind(prototypeOfObject.__lookupGetter__),lookupSetter=call.bind(prototypeOfObject.__lookupSetter__)),2!=[1,2].splice(0).length)if(function(){function makeArray(l){var a=Array(l+2);return a[0]=a[1]=0,a}var lengthBefore,array=[];return array.splice.apply(array,makeArray(20)),array.splice.apply(array,makeArray(26)),lengthBefore=array.length,array.splice(5,0,\"XXX\"),lengthBefore+1==array.length,lengthBefore+1==array.length?!0:void 0\n}()){var array_splice=Array.prototype.splice;Array.prototype.splice=function(start,deleteCount){return arguments.length?array_splice.apply(this,[void 0===start?0:start,void 0===deleteCount?this.length-start:deleteCount].concat(slice.call(arguments,2))):[]}}else Array.prototype.splice=function(pos,removeCount){var length=this.length;pos>0?pos>length&&(pos=length):void 0==pos?pos=0:0>pos&&(pos=Math.max(length+pos,0)),length>pos+removeCount||(removeCount=length-pos);var removed=this.slice(pos,pos+removeCount),insert=slice.call(arguments,2),add=insert.length;if(pos===length)add&&this.push.apply(this,insert);else{var remove=Math.min(removeCount,length-pos),tailOldPos=pos+remove,tailNewPos=tailOldPos+add-remove,tailCount=length-tailOldPos,lengthAfterRemove=length-remove;if(tailOldPos>tailNewPos)for(var i=0;tailCount>i;++i)this[tailNewPos+i]=this[tailOldPos+i];else if(tailNewPos>tailOldPos)for(i=tailCount;i--;)this[tailNewPos+i]=this[tailOldPos+i];if(add&&pos===lengthAfterRemove)this.length=lengthAfterRemove,this.push.apply(this,insert);else for(this.length=lengthAfterRemove+add,i=0;add>i;++i)this[pos+i]=insert[i]}return removed};Array.isArray||(Array.isArray=function(obj){return\"[object Array]\"==_toString(obj)});var boxedString=Object(\"a\"),splitString=\"a\"!=boxedString[0]||!(0 in boxedString);if(Array.prototype.forEach||(Array.prototype.forEach=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,thisp=arguments[1],i=-1,length=self.length>>>0;if(\"[object Function]\"!=_toString(fun))throw new TypeError;for(;length>++i;)i in self&&fun.call(thisp,self[i],i,object)}),Array.prototype.map||(Array.prototype.map=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,result=Array(length),thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)i in self&&(result[i]=fun.call(thisp,self[i],i,object));return result}),Array.prototype.filter||(Array.prototype.filter=function(fun){var value,object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,result=[],thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)i in self&&(value=self[i],fun.call(thisp,value,i,object)&&result.push(value));return result}),Array.prototype.every||(Array.prototype.every=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)if(i in self&&!fun.call(thisp,self[i],i,object))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)if(i in self&&fun.call(thisp,self[i],i,object))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0;if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");if(!length&&1==arguments.length)throw new TypeError(\"reduce of empty array with no initial value\");var result,i=0;if(arguments.length>=2)result=arguments[1];else for(;;){if(i in self){result=self[i++];break}if(++i>=length)throw new TypeError(\"reduce of empty array with no initial value\")}for(;length>i;i++)i in self&&(result=fun.call(void 0,result,self[i],i,object));return result}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0;if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");if(!length&&1==arguments.length)throw new TypeError(\"reduceRight of empty array with no initial value\");var result,i=length-1;if(arguments.length>=2)result=arguments[1];else for(;;){if(i in self){result=self[i--];break}if(0>--i)throw new TypeError(\"reduceRight of empty array with no initial value\")}do i in this&&(result=fun.call(void 0,result,self[i],i,object));while(i--);return result}),Array.prototype.indexOf&&-1==[0,1].indexOf(1,2)||(Array.prototype.indexOf=function(sought){var self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):toObject(this),length=self.length>>>0;if(!length)return-1;var i=0;for(arguments.length>1&&(i=toInteger(arguments[1])),i=i>=0?i:Math.max(0,length+i);length>i;i++)if(i in self&&self[i]===sought)return i;return-1}),Array.prototype.lastIndexOf&&-1==[0,1].lastIndexOf(0,-3)||(Array.prototype.lastIndexOf=function(sought){var self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):toObject(this),length=self.length>>>0;if(!length)return-1;var i=length-1;for(arguments.length>1&&(i=Math.min(i,toInteger(arguments[1]))),i=i>=0?i:length-Math.abs(i);i>=0;i--)if(i in self&&sought===self[i])return i;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(object){return object.__proto__||(object.constructor?object.constructor.prototype:prototypeOfObject)}),!Object.getOwnPropertyDescriptor){var ERR_NON_OBJECT=\"Object.getOwnPropertyDescriptor called on a non-object: \";Object.getOwnPropertyDescriptor=function(object,property){if(\"object\"!=typeof object&&\"function\"!=typeof object||null===object)throw new TypeError(ERR_NON_OBJECT+object);if(owns(object,property)){var descriptor,getter,setter;if(descriptor={enumerable:!0,configurable:!0},supportsAccessors){var prototype=object.__proto__;object.__proto__=prototypeOfObject;var getter=lookupGetter(object,property),setter=lookupSetter(object,property);if(object.__proto__=prototype,getter||setter)return getter&&(descriptor.get=getter),setter&&(descriptor.set=setter),descriptor}return descriptor.value=object[property],descriptor}}}if(Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(object){return Object.keys(object)}),!Object.create){var createEmpty;createEmpty=null===Object.prototype.__proto__?function(){return{__proto__:null}}:function(){var empty={};for(var i in empty)empty[i]=null;return empty.constructor=empty.hasOwnProperty=empty.propertyIsEnumerable=empty.isPrototypeOf=empty.toLocaleString=empty.toString=empty.valueOf=empty.__proto__=null,empty},Object.create=function(prototype,properties){var object;if(null===prototype)object=createEmpty();else{if(\"object\"!=typeof prototype)throw new TypeError(\"typeof prototype[\"+typeof prototype+\"] != 'object'\");var Type=function(){};Type.prototype=prototype,object=new Type,object.__proto__=prototype}return void 0!==properties&&Object.defineProperties(object,properties),object}}if(Object.defineProperty){var definePropertyWorksOnObject=doesDefinePropertyWork({}),definePropertyWorksOnDom=\"undefined\"==typeof document||doesDefinePropertyWork(document.createElement(\"div\"));if(!definePropertyWorksOnObject||!definePropertyWorksOnDom)var definePropertyFallback=Object.defineProperty}if(!Object.defineProperty||definePropertyFallback){var ERR_NON_OBJECT_DESCRIPTOR=\"Property description must be an object: \",ERR_NON_OBJECT_TARGET=\"Object.defineProperty called on non-object: \",ERR_ACCESSORS_NOT_SUPPORTED=\"getters & setters can not be defined on this javascript engine\";Object.defineProperty=function(object,property,descriptor){if(\"object\"!=typeof object&&\"function\"!=typeof object||null===object)throw new TypeError(ERR_NON_OBJECT_TARGET+object);if(\"object\"!=typeof descriptor&&\"function\"!=typeof descriptor||null===descriptor)throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR+descriptor);if(definePropertyFallback)try{return definePropertyFallback.call(Object,object,property,descriptor)}catch(exception){}if(owns(descriptor,\"value\"))if(supportsAccessors&&(lookupGetter(object,property)||lookupSetter(object,property))){var prototype=object.__proto__;object.__proto__=prototypeOfObject,delete object[property],object[property]=descriptor.value,object.__proto__=prototype}else object[property]=descriptor.value;else{if(!supportsAccessors)throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);owns(descriptor,\"get\")&&defineGetter(object,property,descriptor.get),owns(descriptor,\"set\")&&defineSetter(object,property,descriptor.set)}return object}}Object.defineProperties||(Object.defineProperties=function(object,properties){for(var property in properties)owns(properties,property)&&Object.defineProperty(object,property,properties[property]);return object}),Object.seal||(Object.seal=function(object){return object}),Object.freeze||(Object.freeze=function(object){return object});try{Object.freeze(function(){})}catch(exception){Object.freeze=function(freezeObject){return function(object){return\"function\"==typeof object?object:freezeObject(object)}}(Object.freeze)}if(Object.preventExtensions||(Object.preventExtensions=function(object){return object}),Object.isSealed||(Object.isSealed=function(){return!1}),Object.isFrozen||(Object.isFrozen=function(){return!1}),Object.isExtensible||(Object.isExtensible=function(object){if(Object(object)===object)throw new TypeError;for(var name=\"\";owns(object,name);)name+=\"?\";object[name]=!0;var returnValue=owns(object,name);return delete object[name],returnValue}),!Object.keys){var hasDontEnumBug=!0,dontEnums=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"],dontEnumsLength=dontEnums.length;for(var key in{toString:null})hasDontEnumBug=!1;Object.keys=function(object){if(\"object\"!=typeof object&&\"function\"!=typeof object||null===object)throw new TypeError(\"Object.keys called on a non-object\");var keys=[];for(var name in object)owns(object,name)&&keys.push(name);if(hasDontEnumBug)for(var i=0,ii=dontEnumsLength;ii>i;i++){var dontEnum=dontEnums[i];owns(object,dontEnum)&&keys.push(dontEnum)}return keys}}Date.now||(Date.now=function(){return(new Date).getTime()});var ws=\"\t\\n\u000b\\f\\r   ᠎              \\u2028\\u2029\";if(!String.prototype.trim||ws.trim()){ws=\"[\"+ws+\"]\";var trimBeginRegexp=RegExp(\"^\"+ws+ws+\"*\"),trimEndRegexp=RegExp(ws+ws+\"*$\");String.prototype.trim=function(){return(this+\"\").replace(trimBeginRegexp,\"\").replace(trimEndRegexp,\"\")}}var toObject=function(o){if(null==o)throw new TypeError(\"can't convert \"+o+\" to object\");return Object(o)}});"; - -/***/ }, -/* 16 */ -/***/ function(module, exports) { - - ace.define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"], function(acequire, exports, module) { - "use strict"; - - var dom = acequire("../lib/dom"); - var lang = acequire("../lib/lang"); - var event = acequire("../lib/event"); - var searchboxCss = "\ - .ace_search {\ - background-color: #ddd;\ - border: 1px solid #cbcbcb;\ - border-top: 0 none;\ - max-width: 325px;\ - overflow: hidden;\ - margin: 0;\ - padding: 4px;\ - padding-right: 6px;\ - padding-bottom: 0;\ - position: absolute;\ - top: 0px;\ - z-index: 99;\ - white-space: normal;\ - }\ - .ace_search.left {\ - border-left: 0 none;\ - border-radius: 0px 0px 5px 0px;\ - left: 0;\ - }\ - .ace_search.right {\ - border-radius: 0px 0px 0px 5px;\ - border-right: 0 none;\ - right: 0;\ - }\ - .ace_search_form, .ace_replace_form {\ - border-radius: 3px;\ - border: 1px solid #cbcbcb;\ - float: left;\ - margin-bottom: 4px;\ - overflow: hidden;\ - }\ - .ace_search_form.ace_nomatch {\ - outline: 1px solid red;\ - }\ - .ace_search_field {\ - background-color: white;\ - border-right: 1px solid #cbcbcb;\ - border: 0 none;\ - -webkit-box-sizing: border-box;\ - -moz-box-sizing: border-box;\ - box-sizing: border-box;\ - float: left;\ - height: 22px;\ - outline: 0;\ - padding: 0 7px;\ - width: 214px;\ - margin: 0;\ - }\ - .ace_searchbtn,\ - .ace_replacebtn {\ - background: #fff;\ - border: 0 none;\ - border-left: 1px solid #dcdcdc;\ - cursor: pointer;\ - float: left;\ - height: 22px;\ - margin: 0;\ - position: relative;\ - }\ - .ace_searchbtn:last-child,\ - .ace_replacebtn:last-child {\ - border-top-right-radius: 3px;\ - border-bottom-right-radius: 3px;\ - }\ - .ace_searchbtn:disabled {\ - background: none;\ - cursor: default;\ - }\ - .ace_searchbtn {\ - background-position: 50% 50%;\ - background-repeat: no-repeat;\ - width: 27px;\ - }\ - .ace_searchbtn.prev {\ - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); \ - }\ - .ace_searchbtn.next {\ - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); \ - }\ - .ace_searchbtn_close {\ - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\ - border-radius: 50%;\ - border: 0 none;\ - color: #656565;\ - cursor: pointer;\ - float: right;\ - font: 16px/16px Arial;\ - height: 14px;\ - margin: 5px 1px 9px 5px;\ - padding: 0;\ - text-align: center;\ - width: 14px;\ - }\ - .ace_searchbtn_close:hover {\ - background-color: #656565;\ - background-position: 50% 100%;\ - color: white;\ - }\ - .ace_replacebtn.prev {\ - width: 54px\ - }\ - .ace_replacebtn.next {\ - width: 27px\ - }\ - .ace_button {\ - margin-left: 2px;\ - cursor: pointer;\ - -webkit-user-select: none;\ - -moz-user-select: none;\ - -o-user-select: none;\ - -ms-user-select: none;\ - user-select: none;\ - overflow: hidden;\ - opacity: 0.7;\ - border: 1px solid rgba(100,100,100,0.23);\ - padding: 1px;\ - -moz-box-sizing: border-box;\ - box-sizing: border-box;\ - color: black;\ - }\ - .ace_button:hover {\ - background-color: #eee;\ - opacity:1;\ - }\ - .ace_button:active {\ - background-color: #ddd;\ - }\ - .ace_button.checked {\ - border-color: #3399ff;\ - opacity:1;\ - }\ - .ace_search_options{\ - margin-bottom: 3px;\ - text-align: right;\ - -webkit-user-select: none;\ - -moz-user-select: none;\ - -o-user-select: none;\ - -ms-user-select: none;\ - user-select: none;\ - }"; - var HashHandler = acequire("../keyboard/hash_handler").HashHandler; - var keyUtil = acequire("../lib/keys"); - - dom.importCssString(searchboxCss, "ace_searchbox"); - - var html = ''.replace(/>\s+/g, ">"); - - var SearchBox = function(editor, range, showReplaceForm) { - var div = dom.createElement("div"); - div.innerHTML = html; - this.element = div.firstChild; - - this.$init(); - this.setEditor(editor); - }; - - (function() { - this.setEditor = function(editor) { - editor.searchBox = this; - editor.container.appendChild(this.element); - this.editor = editor; - }; - - this.$initElements = function(sb) { - this.searchBox = sb.querySelector(".ace_search_form"); - this.replaceBox = sb.querySelector(".ace_replace_form"); - this.searchOptions = sb.querySelector(".ace_search_options"); - this.regExpOption = sb.querySelector("[action=toggleRegexpMode]"); - this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]"); - this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]"); - this.searchInput = this.searchBox.querySelector(".ace_search_field"); - this.replaceInput = this.replaceBox.querySelector(".ace_search_field"); - }; - - this.$init = function() { - var sb = this.element; - - this.$initElements(sb); - - var _this = this; - event.addListener(sb, "mousedown", function(e) { - setTimeout(function(){ - _this.activeInput.focus(); - }, 0); - event.stopPropagation(e); - }); - event.addListener(sb, "click", function(e) { - var t = e.target || e.srcElement; - var action = t.getAttribute("action"); - if (action && _this[action]) - _this[action](); - else if (_this.$searchBarKb.commands[action]) - _this.$searchBarKb.commands[action].exec(_this); - event.stopPropagation(e); - }); - - event.addCommandKeyListener(sb, function(e, hashId, keyCode) { - var keyString = keyUtil.keyCodeToString(keyCode); - var command = _this.$searchBarKb.findKeyCommand(hashId, keyString); - if (command && command.exec) { - command.exec(_this); - event.stopEvent(e); - } - }); - - this.$onChange = lang.delayedCall(function() { - _this.find(false, false); - }); - - event.addListener(this.searchInput, "input", function() { - _this.$onChange.schedule(20); - }); - event.addListener(this.searchInput, "focus", function() { - _this.activeInput = _this.searchInput; - _this.searchInput.value && _this.highlight(); - }); - event.addListener(this.replaceInput, "focus", function() { - _this.activeInput = _this.replaceInput; - _this.searchInput.value && _this.highlight(); - }); - }; - this.$closeSearchBarKb = new HashHandler([{ - bindKey: "Esc", - name: "closeSearchBar", - exec: function(editor) { - editor.searchBox.hide(); - } - }]); - this.$searchBarKb = new HashHandler(); - this.$searchBarKb.bindKeys({ - "Ctrl-f|Command-f": function(sb) { - var isReplace = sb.isReplace = !sb.isReplace; - sb.replaceBox.style.display = isReplace ? "" : "none"; - sb.searchInput.focus(); - }, - "Ctrl-H|Command-Option-F": function(sb) { - sb.replaceBox.style.display = ""; - sb.replaceInput.focus(); - }, - "Ctrl-G|Command-G": function(sb) { - sb.findNext(); - }, - "Ctrl-Shift-G|Command-Shift-G": function(sb) { - sb.findPrev(); - }, - "esc": function(sb) { - setTimeout(function() { sb.hide();}); - }, - "Return": function(sb) { - if (sb.activeInput == sb.replaceInput) - sb.replace(); - sb.findNext(); - }, - "Shift-Return": function(sb) { - if (sb.activeInput == sb.replaceInput) - sb.replace(); - sb.findPrev(); - }, - "Alt-Return": function(sb) { - if (sb.activeInput == sb.replaceInput) - sb.replaceAll(); - sb.findAll(); - }, - "Tab": function(sb) { - (sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus(); - } - }); - - this.$searchBarKb.addCommands([{ - name: "toggleRegexpMode", - bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"}, - exec: function(sb) { - sb.regExpOption.checked = !sb.regExpOption.checked; - sb.$syncOptions(); - } - }, { - name: "toggleCaseSensitive", - bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"}, - exec: function(sb) { - sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked; - sb.$syncOptions(); - } - }, { - name: "toggleWholeWords", - bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"}, - exec: function(sb) { - sb.wholeWordOption.checked = !sb.wholeWordOption.checked; - sb.$syncOptions(); - } - }]); - - this.$syncOptions = function() { - dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked); - dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked); - dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked); - this.find(false, false); - }; - - this.highlight = function(re) { - this.editor.session.highlight(re || this.editor.$search.$options.re); - this.editor.renderer.updateBackMarkers() - }; - this.find = function(skipCurrent, backwards, preventScroll) { - var range = this.editor.find(this.searchInput.value, { - skipCurrent: skipCurrent, - backwards: backwards, - wrap: true, - regExp: this.regExpOption.checked, - caseSensitive: this.caseSensitiveOption.checked, - wholeWord: this.wholeWordOption.checked, - preventScroll: preventScroll - }); - var noMatch = !range && this.searchInput.value; - dom.setCssClass(this.searchBox, "ace_nomatch", noMatch); - this.editor._emit("findSearchBox", { match: !noMatch }); - this.highlight(); - }; - this.findNext = function() { - this.find(true, false); - }; - this.findPrev = function() { - this.find(true, true); - }; - this.findAll = function(){ - var range = this.editor.findAll(this.searchInput.value, { - regExp: this.regExpOption.checked, - caseSensitive: this.caseSensitiveOption.checked, - wholeWord: this.wholeWordOption.checked - }); - var noMatch = !range && this.searchInput.value; - dom.setCssClass(this.searchBox, "ace_nomatch", noMatch); - this.editor._emit("findSearchBox", { match: !noMatch }); - this.highlight(); - this.hide(); - }; - this.replace = function() { - if (!this.editor.getReadOnly()) - this.editor.replace(this.replaceInput.value); - }; - this.replaceAndFindNext = function() { - if (!this.editor.getReadOnly()) { - this.editor.replace(this.replaceInput.value); - this.findNext() - } - }; - this.replaceAll = function() { - if (!this.editor.getReadOnly()) - this.editor.replaceAll(this.replaceInput.value); - }; - - this.hide = function() { - this.element.style.display = "none"; - this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb); - this.editor.focus(); - }; - this.show = function(value, isReplace) { - this.element.style.display = ""; - this.replaceBox.style.display = isReplace ? "" : "none"; - - this.isReplace = isReplace; - - if (value) - this.searchInput.value = value; - - this.find(false, false, true); - - this.searchInput.focus(); - this.searchInput.select(); - - this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb); - }; - - this.isFocused = function() { - var el = document.activeElement; - return el == this.searchInput || el == this.replaceInput; - } - }).call(SearchBox.prototype); - - exports.SearchBox = SearchBox; - - exports.Search = function(editor, isReplace) { - var sb = editor.searchBox || new SearchBox(editor); - sb.show(editor.session.getTextRange(), isReplace); - }; - - }); - (function() { - ace.acequire(["ace/ext/searchbox"], function() {}); - })(); - - -/***/ }, -/* 17 */ -/***/ function(module, exports) { - - /* ***** 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); - }); - - -/***/ } -/******/ ]) -}); -; \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.jsoneditor=e():t.jsoneditor=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return t[o].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){var n=(0,r.render)((0,r.h)(a["default"],{options:e}),t);return n._component}var r=n(1),s=n(4),a=o(s);n(14),t.exports=i},function(t,e,n){(function(t){!function(t,n){n(e)}(this,function(t){function e(t,e,n){this.nodeName=t,this.attributes=e,this.children=n,this.key=e&&e.key}function n(t,e){if(e)for(var n in e)void 0!==e[n]&&(t[n]=e[n]);return t}function o(t){return n({},t)}function i(t,e){for(var n=e.split("."),o=0;o2){var f=typeof o;if(3===u&&"object"!==f&&"function"!==f)c(o)||(i=[String(o)]);else{i=[];for(var h=2;u>h;h++){var y=arguments[h];if(!c(y)){y.join?r=y:(r=Q)[0]=y;for(var m=0;m2?r(arguments,2):t.children)}function f(t,e,n){var o=e.split("."),r=o[0];return function(e){var c,d,p,u=e&&e.currentTarget||this,f=t.state,h=f;if(a(n)?(d=i(e,n),l(d)&&(u=u._component)&&(d=i(u,n))):d=u.nodeName?(u.nodeName+u.type).match(/^input(check|rad)/i)?u.checked:u.value:e,s(d)&&(d=d.call(u)),o.length>1){for(p=0;pv;v++){var k=c[v],g=m?(r=k._component)?r.__key:(r=k[tt])?r.key:null:null;g||0===g?(u++,p[g]=k):d[y++]=k}if(m)for(var v=0;m>v;v++){if(s=e[v],a=null,u&&s.attributes){var g=s.key;!l(g)&&g in p&&(a=p[g],p[g]=void 0,u--)}if(!a&&y>f)for(i=f;y>i;i++)if(r=d[i],r&&E(r,s)){a=r,d[i]=void 0,i===y-1&&y--,i===f&&f++;break}a=A(a,s,n,o),a!==c[v]&&t.insertBefore(a,c[v]||null)}if(u)for(var v in p)p[v]&&(d[f=y++]=p[v]);y>f&&z(d)}function z(t,e){for(var n=t.length;n--;){var o=t[n];o&&R(o,e)}}function R(t,e){var n=t._component;n?J(n,!e):(t[tt]&&t[tt].ref&&t[tt].ref(null),e||N(t),t.childNodes&&t.childNodes.length&&z(t.childNodes,e))}function B(t,e){var n=t[tt]||C(t);for(var o in n)e&&o in e||x(t,o,null,n[o],at);if(e)for(var i in e)i in n&&e[i]==("value"===i||"selected"===i||"checked"===i?t[i]:n[i])||x(t,i,e[i],n[i],at)}function L(t){var e=t.constructor.name,n=lt[e];n?n.push(t):lt[e]=[t]}function V(t,e,n){var o=new t(e,n),i=lt[t.name];if(i)for(var r=i.length;r--;)if(i[r].constructor===t){o.nextBase=i[r].nextBase,i.splice(r,1);break}return o}function U(t){t._dirty||(t._dirty=!0,h(t))}function D(t,e,n,o,i){var r=t.base;t._disableRendering||(t._disableRendering=!0,(t.__ref=e.ref)&&delete e.ref,(t.__key=e.key)&&delete e.key,l(r)||i?t.componentWillMount&&t.componentWillMount():t.componentWillReceiveProps&&t.componentWillReceiveProps(e,o),o&&o!==t.context&&(t.prevContext||(t.prevContext=t.context),t.context=o),t.prevProps||(t.prevProps=t.props),t.props=e,t._disableRendering=!1,0!==n&&(1!==n&&X.syncComponentUpdates===!1&&r?U(t):F(t,1,i)),t.__ref&&t.__ref(t))}function F(t,e,i){if(!t._disableRendering){var r,a,l=t.props,c=t.state,d=t.context,p=t.prevProps||l,u=t.prevState||c,f=t.prevContext||d,h=t.base,y=h||t.nextBase,k=y&&y.nextSibling,g=y&&y.parentNode,b=y&&y._component,x=t._component;if(h&&(t.props=p,t.state=u,t.context=f,2!==e&&t.shouldComponentUpdate&&t.shouldComponentUpdate(l,c,d)===!1?r=!0:t.componentWillUpdate&&t.componentWillUpdate(l,c,d),t.props=l,t.state=c,t.context=d),t.prevProps=t.prevState=t.prevContext=t.nextBase=null,t._dirty=!1,!r){for(t.render&&(a=t.render(l,c,d)),t.getChildContext&&(d=n(o(d),t.getChildContext()));m(a);)a=v(a,d);var j,w,C=a&&a.nodeName;if(s(C)&&C.prototype.render){var E=x,_=S(a);E&&E.constructor===C?D(E,_,1,d):(j=E,E=V(C,_,d),E._parentComponent=t,t._component=E,D(E,_,0,d),F(E,1)),w=E.base}else{var N=y;j=x,j&&(N=t._component=null),(y||1===e)&&(N&&(N._component=null),w=M(N,a,d,i||!h))}if(y&&w!==y&&(g&&w!==g&&g.insertBefore(w,k||null),j||b!==t||x||!y.parentNode||(y._component=null,R(y))),j&&J(j,!0),t.base=w,w){for(var I=t,O=t;O=O._parentComponent;)I=O;w._component=I,w._componentConstructor=I.constructor}}!h||i?(rt.unshift(t),st||T()):!r&&t.componentDidUpdate&&t.componentDidUpdate(p,u,f);var A,P=t._renderCallbacks;if(P)for(;A=P.pop();)A.call(t);return a}}function H(t,e,n,o){for(var i=t&&t._component,r=t,s=i&&t._componentConstructor===e.nodeName,a=s,l=S(e);i&&!a&&(i=i._parentComponent);)a=i.constructor===e.nodeName;return!a||o&&!i._component?(i&&!s&&(J(i,!0),t=r=null),i=V(e.nodeName,l,n),t&&!i.nextBase&&(i.nextBase=t),D(i,l,1,n,o),t=i.base,r&&t!==r&&(r._component=null,R(r))):(D(i,l,3,n,o),t=i.base),t}function J(t,e){var n=t.base;t._disableRendering=!0,t.componentWillUnmount&&t.componentWillUnmount(),t.base=null;var o=t._component;o?J(o,e):n&&(n[tt]&&n[tt].ref&&n[tt].ref(null),t.nextBase=n,e&&(b(n),L(t)),z(n.childNodes,!e)),t.__ref&&t.__ref(null),t.componentDidUnmount&&t.componentDidUnmount()}function G(t,e){this._dirty=!0,this._disableRendering=!1,this.prevState=this.prevProps=this.prevContext=this.base=this.nextBase=this._parentComponent=this._component=this.__ref=this.__key=this._linkedStates=this._renderCallbacks=null,this.context=e||{},this.props=t,this.state=this.getInitialState&&this.getInitialState()||{}}function W(t,e,n){return M(n,t,{},!1,e)}var K={},Z=function(t){return K[t]||(K[t]=t.toLowerCase())},$="undefined"!=typeof Promise&&Promise.resolve(),q=$?function(t){$.then(t)}:setTimeout,X={vnode:l},Q=[],Y={},tt="undefined"!=typeof Symbol?Symbol["for"]("preactattr"):"__preactattr_",et={boxFlex:1,boxFlexGroup:1,columnCount:1,fillOpacity:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,fontWeight:1,lineClamp:1,lineHeight:1,opacity:1,order:1,orphans:1,strokeOpacity:1,widows:1,zIndex:1,zoom:1},nt=[],ot=[],it={},rt=[],st=0,at=!1,lt={};n(G.prototype,{linkState:function(t,e){var n=this._linkedStates||(this._linkedStates={}),o=t+"|"+e;return n[o]||(n[o]=f(this,t,e))},setState:function(t,e){var i=this.state;this.prevState||(this.prevState=o(i)),n(i,s(t)?t(i,this.props):t),e&&(this._renderCallbacks=this._renderCallbacks||[]).push(e),U(this)},forceUpdate:function(){F(this,2)},render:function(){return null}}),t.h=p,t.cloneElement=u,t.Component=G,t.render=W,t.rerender=y,t.options=X})}).call(e,n(2).setImmediate)},function(t,e,n){(function(t,o){function i(t,e){this._id=t,this._clearFn=e}var r=n(3).nextTick,s=Function.prototype.apply,a=Array.prototype.slice,l={},c=0;e.setTimeout=function(){return new i(s.call(setTimeout,window,arguments),clearTimeout)},e.setInterval=function(){return new i(s.call(setInterval,window,arguments),clearInterval)},e.clearTimeout=e.clearInterval=function(t){t.close()},i.prototype.unref=i.prototype.ref=function(){},i.prototype.close=function(){this._clearFn.call(window,this._id)},e.enroll=function(t,e){clearTimeout(t._idleTimeoutId),t._idleTimeout=e},e.unenroll=function(t){clearTimeout(t._idleTimeoutId),t._idleTimeout=-1},e._unrefActive=e.active=function(t){clearTimeout(t._idleTimeoutId);var e=t._idleTimeout;e>=0&&(t._idleTimeoutId=setTimeout(function(){t._onTimeout&&t._onTimeout()},e))},e.setImmediate="function"==typeof t?t:function(t){var n=c++,o=arguments.length<2?!1:a.call(arguments,1);return l[n]=!0,r(function(){l[n]&&(o?t.apply(null,o):t.call(null),e.clearImmediate(n))}),n},e.clearImmediate="function"==typeof o?o:function(t){delete l[t]}}).call(e,n(2).setImmediate,n(2).clearImmediate)},function(t,e){function n(t){if(l===setTimeout)return setTimeout(t,0);try{return l(t,0)}catch(e){try{return l.call(null,t,0)}catch(e){return l.call(this,t,0)}}}function o(t){if(c===clearTimeout)return clearTimeout(t);try{return c(t)}catch(e){try{return c.call(null,t)}catch(e){return c.call(this,t)}}}function i(){f&&p&&(f=!1,p.length?u=p.concat(u):h=-1,u.length&&r())}function r(){if(!f){var t=n(i);f=!0;for(var e=u.length;e;){for(p=u,u=[];++h1)for(var o=1;oe?1:e>t?-1:0}function i(t,e){return t>e?-1:e>t?1:0}Object.defineProperty(e,"__esModule",{value:!0}),e.last=n,e.compareAsc=o,e.compareDesc=i},function(t,e){"use strict";function n(t){var e=Object.getPrototypeOf(t);Object.getOwnPropertyNames(e).forEach(function(e){"function"==typeof t[e]&&(t[e]=t[e].bind(t))})}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n},function(t,e,n){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function r(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function s(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;nl?"top":"bottom";return n.state={orientation:a,expanded:null,expanding:null,collapsing:null},n.renderMenuItem=n.renderMenuItem.bind(n),n}return r(e,t),s(e,[{key:"render",value:function(){if(!this.props.items)return null;var t="jsoneditor-contextmenu "+("top"===this.state.orientation?"jsoneditor-contextmenu-top":"jsoneditor-contextmenu-bottom");return(0,a.h)("div",{"class":t},this.props.items.map(this.renderMenuItem))}},{key:"renderMenuItem",value:function(t,e){return"separator"===t.type?(0,a.h)("div",{"class":"jsoneditor-menu-separator"}):t.click&&t.submenu?(0,a.h)("div",{"class":"jsoneditor-menu-item"},[(0,a.h)("button",{"class":"jsoneditor-menu-button jsoneditor-menu-default "+t.className,title:t.title,onClick:t.click},[(0,a.h)("span",{"class":"jsoneditor-icon"}),(0,a.h)("span",{"class":"jsoneditor-text"},t.text)]),(0,a.h)("button",{"class":"jsoneditor-menu-button jsoneditor-menu-expand",onClick:this.createExpandHandler(e)},[(0,a.h)("span",{"class":"jsoneditor-icon jsoneditor-icon-expand"})]),this.renderSubMenu(t.submenu,e)]):t.submenu?(0,a.h)("div",{"class":"jsoneditor-menu-item"},[(0,a.h)("button",{"class":"jsoneditor-menu-button "+t.className,title:t.title,onClick:this.createExpandHandler(e)},[(0,a.h)("span",{"class":"jsoneditor-icon"}),(0,a.h)("span",{"class":"jsoneditor-text"},t.text),(0,a.h)("span",{"class":"jsoneditor-icon jsoneditor-icon-expand"})]),this.renderSubMenu(t.submenu,e)]):(0,a.h)("div",{"class":"jsoneditor-menu-item"},[(0,a.h)("button",{"class":"jsoneditor-menu-button "+t.className,title:t.title,onClick:t.click},[(0,a.h)("span",{"class":"jsoneditor-icon"}),(0,a.h)("span",{"class":"jsoneditor-text"},t.text)])])}},{key:"renderSubMenu",value:function(t,e){var n=this.state.expanded===e,o=this.state.collapsing===e,i=t.map(function(t){return(0,a.h)("div",{"class":"jsoneditor-menu-item"},[(0,a.h)("button",{"class":"jsoneditor-menu-button "+t.className,title:t.title,onClick:t.click},[(0,a.h)("span",{"class":"jsoneditor-icon"}),(0,a.h)("span",{"class":"jsoneditor-text"},t.text)])])}),r="jsoneditor-submenu "+(n?" jsoneditor-expanded":"")+(o?" jsoneditor-collapsing":"");return(0,a.h)("div",{"class":r},i)}},{key:"createExpandHandler",value:function(t){var e=this;return function(n){n.stopPropagation();var o=e.state.expanded;e.setState({expanded:o===t?null:t,collapsing:o}),setTimeout(function(){o===e.state.collapsing&&e.setState({collapsing:null})},300)}}}]),e}(a.Component);e["default"]=c},function(t,e,n){"use strict";function o(t){var e=arguments.length<=1||void 0===arguments[1]?!1:arguments[1];if("string"!=typeof t)return String(t);var n=String(t).replace(/ /g,"  ").replace(/^ /," ").replace(/ $/," "),o=JSON.stringify(n),r=o.substring(1,o.length-1);return e===!0&&(r=i(r)),r}function i(t){return t.replace(/[\u007F-\uFFFF]/g,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})}function r(t){var e='"'+s(t)+'"',n=(0,a.parseJSON)(e);return n.replace(/\u00A0/g," ")}function s(t){for(var e="",n=0;na;a++){var c=r[a];if("DIV"==c.nodeName||"P"==c.nodeName){var d=r[a-1],p=d?d.nodeName:void 0;p&&"DIV"!=p&&"P"!=p&&"BR"!=p&&(s+="\n",e.flush()),s+=n(c,e),e.set("\n")}else"BR"==c.nodeName?(s+=e.flush(),e.set("\n")):s+=n(c,e)}return s}return"P"==t.nodeName&&-1!=o()?e.flush():""}function o(){if(-1==i){var t=-1;if("Microsoft Internet Explorer"==navigator.appName){var e=navigator.userAgent,n=new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})");null!=n.exec(e)&&(t=parseFloat(RegExp.$1))}i=t}return i}Object.defineProperty(e,"__esModule",{value:!0}),e.getInnerText=n,e.getInternetExplorerVersion=o;var i=-1},function(t,e,n){var o=n(15);"string"==typeof o&&(o=[[t.id,o,""]]);n(18)(o,{});o.locals&&(t.exports=o.locals)},function(t,e,n){e=t.exports=n(16)(),e.push([t.id,".jsoneditor{border:1px solid #3883fa;overflow:auto;height:100%;min-height:150px;padding:2px 0}.jsoneditor-node{position:relative;font:14px Arial;display:inline-flex;flex-direction:row}.jsoneditor-node>div{flex:1 1 auto}ul.jsoneditor-list{list-style-type:none;padding-left:20px;margin:0;font-size:0}.jsoneditor>ul.jsoneditor-list{padding-left:2px}.jsoneditor-property,.jsoneditor-readonly,.jsoneditor-separator,.jsoneditor-value{flex:1 1 auto;line-height:20px;font-family:droid sans mono,consolas,monospace,courier new,courier,sans-serif;font-size:10pt}.jsoneditor-property,.jsoneditor-readonly,.jsoneditor-value{min-width:24px;word-break:break-word;padding:0 5px;color:#1a1a1a;outline:none}.jsoneditor-button-container{font-size:0}.jsoneditor-property,.jsoneditor-value{border-radius:1px}.jsoneditor-property:focus,.jsoneditor-value:focus{box-shadow:0 0 3px 1px #008fd5}.jsoneditor-property:hover,.jsoneditor-value:hover{background-color:#f5f5f5}.jsoneditor-readonly,.jsoneditor-separator{color:gray}.jsoneditor-readonly:focus,.jsoneditor-readonly:hover{border-color:transparent;background-color:inherit}.jsoneditor-value.jsoneditor-string{color:green}.jsoneditor-value.jsoneditor-array,.jsoneditor-value.jsoneditor-object{min-width:16px;color:gray}.jsoneditor-value.jsoneditor-number{color:#ee422e}.jsoneditor-value.jsoneditor-boolean{color:#ff8c00}.jsoneditor-value.jsoneditor-null{color:#004ed0}.jsoneditor-value.jsoneditor-invalid{color:#000}div.jsoneditor-value.jsoneditor-url{color:green;text-decoration:underline}.jsoneditor-button-placeholder{width:20px;padding:0;margin:0;line-height:20px}button.jsoneditor-button{position:relative;width:20px;height:20px;padding:0;margin:0;border:none;cursor:pointer;background:transparent url("+n(17)+")}button.jsoneditor-button:focus{background-color:#f5f5f5;outline:1px solid #e5e5e5}button.jsoneditor-button.jsoneditor-collapsed{background-position:-2px -50px}button.jsoneditor-button.jsoneditor-expanded{background-position:-2px -74px}button.jsoneditor-button.jsoneditor-contextmenu{background-position:-50px -74px}button.jsoneditor-button.jsoneditor-contextmenu.jsoneditor-visible,button.jsoneditor-button.jsoneditor-contextmenu:focus,button.jsoneditor-button.jsoneditor-contextmenu:hover{background-position:-50px -50px}div.jsoneditor-contextmenu{position:absolute;box-sizing:border-box;z-index:99999;top:20px;left:18px;background:#fff;border:1px solid #d3d3d3;box-shadow:2px 2px 12px hsla(0,0%,50%,.3)}div.jsoneditor-contextmenu.jsoneditor-contextmenu-top{top:auto;bottom:20px}div.jsoneditor-menu-item{line-height:0;font-size:0}button.jsoneditor-menu-button{width:136px;height:24px;padding:0;margin:0;line-height:24px;background:transparent;border:transparent;display:inline-block;box-sizing:border-box;cursor:pointer;color:#4d4d4d;font-size:10pt;font-family:arial,sans-serif;text-align:left}button.jsoneditor-menu-button:focus,button.jsoneditor-menu-button:hover{color:#1a1a1a;background-color:#f5f5f5;outline:none}button.jsoneditor-menu-button.jsoneditor-selected{color:#fff;background-color:#ee422e}button.jsoneditor-menu-default{width:104px}button.jsoneditor-menu-expand{width:32px;float:right;border-left:1px solid #e5e5e5}span.jsoneditor-icon{float:left;width:24px;height:24px;border:none;padding:0;margin:0;background-image:url("+n(17)+")}span.jsoneditor-icon.jsoneditor-icon-expand{float:right;width:24px;margin:0 4px;background-position:0 -72px!important;opacity:.4}div.jsoneditor-menu-item button.jsoneditor-menu-button:hover span.jsoneditor-icon-expand,div.jsoneditor-menu-item button:focus span.jsoneditor-icon-expand{opacity:1}span.jsoneditor-text{display:inline-block;line-height:24px}div.jsoneditor-menu-separator{height:0;border-top:1px solid #e5e5e5;padding-top:5px;margin-top:5px}button.jsoneditor-remove span.jsoneditor-icon{background-position:-24px -24px}button.jsoneditor-remove:focus span.jsoneditor-icon,button.jsoneditor-remove:hover span.jsoneditor-icon{background-position:-24px 0}button.jsoneditor-insert span.jsoneditor-icon{background-position:0 -24px}button.jsoneditor-insert:focus span.jsoneditor-icon,button.jsoneditor-insert:hover span.jsoneditor-icon{background-position:0 0}button.jsoneditor-duplicate span.jsoneditor-icon{background-position:-48px -24px}button.jsoneditor-duplicate:focus span.jsoneditor-icon,button.jsoneditor-duplicate:hover span.jsoneditor-icon{background-position:-48px 0}button.jsoneditor-sort-asc span.jsoneditor-icon{background-position:-168px -24px}button.jsoneditor-sort-asc:focus span.jsoneditor-icon,button.jsoneditor-sort-asc:hover span.jsoneditor-icon{background-position:-168px 0}button.jsoneditor-sort-desc span.jsoneditor-icon{background-position:-192px -24px}button.jsoneditor-sort-desc:focus span.jsoneditor-icon,button.jsoneditor-sort-desc:hover span.jsoneditor-icon{background-position:-192px 0}div.jsoneditor-submenu{visibility:hidden;max-height:0;overflow:hidden;transition:max-height .3s ease-out;box-shadow:inset 0 10px 10px -10px hsla(0,0%,50%,.5),inset 0 -10px 10px -10px hsla(0,0%,50%,.5)}div.jsoneditor-submenu.jsoneditor-expanded{visibility:visible;max-height:104px}div.jsoneditor-submenu.jsoneditor-collapsing{visibility:visible;max-height:0}div.jsoneditor-submenu button{padding-left:24px}div.jsoneditor-submenu div.jsoneditor-menu-item:first-child{margin-top:5px}div.jsoneditor-submenu div.jsoneditor-menu-item:last-child{margin-bottom:5px}button.jsoneditor-type-string span.jsoneditor-icon{background-position:-144px -24px}button.jsoneditor-type-string.jsoneditor-selected span.jsoneditor-icon,button.jsoneditor-type-string:focus span.jsoneditor-icon,button.jsoneditor-type-string:hover span.jsoneditor-icon{background-position:-144px 0}button.jsoneditor-type-value span.jsoneditor-icon{background-position:-120px -24px}button.jsoneditor-type-value.jsoneditor-selected span.jsoneditor-icon,button.jsoneditor-type-value:focus span.jsoneditor-icon,button.jsoneditor-type-value:hover span.jsoneditor-icon{background-position:-120px 0}button.jsoneditor-type-object span.jsoneditor-icon{background-position:-72px -24px}button.jsoneditor-type-object.jsoneditor-selected span.jsoneditor-icon,button.jsoneditor-type-object:focus span.jsoneditor-icon,button.jsoneditor-type-object:hover span.jsoneditor-icon{background-position:-72px 0}button.jsoneditor-type-array span.jsoneditor-icon{background-position:-96px -24px}button.jsoneditor-type-array.jsoneditor-selected span.jsoneditor-icon,button.jsoneditor-type-array:focus span.jsoneditor-icon,button.jsoneditor-type-array:hover span.jsoneditor-icon{background-position:-96px 0}",""])},function(t,e){t.exports=function(){var t=[];return t.toString=function(){for(var t=[],e=0;e=0&&g.splice(e,1)}function a(t){var e=document.createElement("style");return e.type="text/css",r(t,e),e}function l(t){var e=document.createElement("link");return e.rel="stylesheet",r(t,e),e}function c(t,e){var n,o,i;if(e.singleton){var r=k++;n=v||(v=a(e)),o=d.bind(null,n,r,!1),i=d.bind(null,n,r,!0)}else t.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=l(e),o=u.bind(null,n),i=function(){s(n),n.href&&URL.revokeObjectURL(n.href)}):(n=a(e),o=p.bind(null,n),i=function(){s(n)});return o(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;o(t=e)}else i()}}function d(t,e,n,o){var i=n?"":o.css;if(t.styleSheet)t.styleSheet.cssText=b(e,i);else{var r=document.createTextNode(i),s=t.childNodes;s[e]&&t.removeChild(s[e]),s.length?t.insertBefore(r,s[e]):t.appendChild(r)}}function p(t,e){var n=e.css,o=e.media;if(o&&t.setAttribute("media",o),t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}function u(t,e){var n=e.css,o=e.sourceMap;o&&(n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var i=new Blob([n],{type:"text/css"}),r=t.href;t.href=URL.createObjectURL(i),r&&URL.revokeObjectURL(r)}var f={},h=function(t){var e;return function(){return"undefined"==typeof e&&(e=t.apply(this,arguments)),e}},y=h(function(){return/msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())}),m=h(function(){return document.head||document.getElementsByTagName("head")[0]}),v=null,k=0,g=[];t.exports=function(t,e){e=e||{},"undefined"==typeof e.singleton&&(e.singleton=y()),"undefined"==typeof e.insertAt&&(e.insertAt="bottom");var n=i(t);return o(n,e),function(t){for(var r=[],s=0;s\n * @version 6.0.0-BETA\n * @date 2016-08-13\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"jsoneditor\"] = factory();\n\telse\n\t\troot[\"jsoneditor\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar _preact = __webpack_require__(1);\n\t\n\tvar _TreeMode = __webpack_require__(4);\n\t\n\tvar _TreeMode2 = _interopRequireDefault(_TreeMode);\n\t\n\t__webpack_require__(14);\n\t\n\tfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\t\n\t/**\n\t * Factory function to create a new JSONEditor\n\t * @param container\n\t * @param {Options} options\n\t * @return {*}\n\t * @constructor\n\t */\n\tfunction jsoneditor(container, options) {\n\t // TODO: use JSONEditor instead of TreeMode\n\t var elem = (0, _preact.render)((0, _preact.h)(_TreeMode2.default, { options: options }), container);\n\t return elem._component;\n\t}\n\t\n\t// TODO: use export default jsoneditor, doesn't work out of the box in webpack\n\tmodule.exports = jsoneditor;\n\n/***/ },\n/* 1 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(setImmediate) {!function(global, factory) {\n\t true ? factory(exports) : 'function' == typeof define && define.amd ? define([ 'exports' ], factory) : factory(global.preact = global.preact || {});\n\t}(this, function(exports) {\n\t function VNode(nodeName, attributes, children) {\n\t this.nodeName = nodeName;\n\t this.attributes = attributes;\n\t this.children = children;\n\t this.key = attributes && attributes.key;\n\t }\n\t function extend(obj, props) {\n\t if (props) for (var i in props) if (void 0 !== props[i]) obj[i] = props[i];\n\t return obj;\n\t }\n\t function clone(obj) {\n\t return extend({}, obj);\n\t }\n\t function delve(obj, key) {\n\t for (var p = key.split('.'), i = 0; i < p.length && obj; i++) obj = obj[p[i]];\n\t return obj;\n\t }\n\t function toArray(obj, offset) {\n\t return [].slice.call(obj, offset);\n\t }\n\t function isFunction(obj) {\n\t return 'function' == typeof obj;\n\t }\n\t function isString(obj) {\n\t return 'string' == typeof obj;\n\t }\n\t function empty(x) {\n\t return void 0 === x || null === x;\n\t }\n\t function falsey(value) {\n\t return value === !1 || empty(value);\n\t }\n\t function hashToClassName(c) {\n\t var str = '';\n\t for (var prop in c) if (c[prop]) {\n\t if (str) str += ' ';\n\t str += prop;\n\t }\n\t return str;\n\t }\n\t function h(nodeName, attributes, firstChild) {\n\t var children, arr, lastSimple, len = arguments.length;\n\t if (len > 2) {\n\t var type = typeof firstChild;\n\t if (3 === len && 'object' !== type && 'function' !== type) {\n\t if (!falsey(firstChild)) children = [ String(firstChild) ];\n\t } else {\n\t children = [];\n\t for (var i = 2; i < len; i++) {\n\t var _p = arguments[i];\n\t if (!falsey(_p)) {\n\t if (_p.join) arr = _p; else (arr = SHARED_TEMP_ARRAY)[0] = _p;\n\t for (var j = 0; j < arr.length; j++) {\n\t var child = arr[j], simple = !(falsey(child) || isFunction(child) || child instanceof VNode);\n\t if (simple && !isString(child)) child = String(child);\n\t if (simple && lastSimple) children[children.length - 1] += child; else if (!falsey(child)) {\n\t children.push(child);\n\t lastSimple = simple;\n\t }\n\t }\n\t } else ;\n\t }\n\t }\n\t } else if (attributes && attributes.children) return h(nodeName, attributes, attributes.children);\n\t if (attributes) {\n\t if (attributes.children) delete attributes.children;\n\t if (!isFunction(nodeName)) {\n\t if ('className' in attributes) {\n\t attributes.class = attributes.className;\n\t delete attributes.className;\n\t }\n\t lastSimple = attributes.class;\n\t if (lastSimple && !isString(lastSimple)) attributes.class = hashToClassName(lastSimple);\n\t }\n\t }\n\t var p = new VNode(nodeName, attributes || void 0, children);\n\t if (options.vnode) options.vnode(p);\n\t return p;\n\t }\n\t function cloneElement(vnode, props) {\n\t return h(vnode.nodeName, extend(clone(vnode.attributes), props), arguments.length > 2 ? toArray(arguments, 2) : vnode.children);\n\t }\n\t function createLinkedState(component, key, eventPath) {\n\t var path = key.split('.'), p0 = path[0];\n\t return function(e) {\n\t var _component$setState;\n\t var v, i, t = e && e.currentTarget || this, s = component.state, obj = s;\n\t if (isString(eventPath)) {\n\t v = delve(e, eventPath);\n\t if (empty(v) && (t = t._component)) v = delve(t, eventPath);\n\t } else v = t.nodeName ? (t.nodeName + t.type).match(/^input(check|rad)/i) ? t.checked : t.value : e;\n\t if (isFunction(v)) v = v.call(t);\n\t if (path.length > 1) {\n\t for (i = 0; i < path.length - 1; i++) obj = obj[path[i]] || (obj[path[i]] = {});\n\t obj[path[i]] = v;\n\t v = s[p0];\n\t }\n\t component.setState((_component$setState = {}, _component$setState[p0] = v, _component$setState));\n\t };\n\t }\n\t function enqueueRender(component) {\n\t if (1 === items.push(component)) (options.debounceRendering || setImmediate)(rerender);\n\t }\n\t function rerender() {\n\t if (items.length) {\n\t var p, currentItems = items;\n\t items = itemsOffline;\n\t itemsOffline = currentItems;\n\t while (p = currentItems.pop()) if (p._dirty) renderComponent(p);\n\t }\n\t }\n\t function isFunctionalComponent(vnode) {\n\t var nodeName = vnode && vnode.nodeName;\n\t return nodeName && isFunction(nodeName) && !(nodeName.prototype && nodeName.prototype.render);\n\t }\n\t function buildFunctionalComponent(vnode, context) {\n\t return vnode.nodeName(getNodeProps(vnode), context || EMPTY);\n\t }\n\t function ensureNodeData(node, data) {\n\t return node[ATTR_KEY] || (node[ATTR_KEY] = data || {});\n\t }\n\t function getNodeType(node) {\n\t if (node instanceof Text) return 3;\n\t if (node instanceof Element) return 1; else return 0;\n\t }\n\t function removeNode(node) {\n\t var p = node.parentNode;\n\t if (p) p.removeChild(node);\n\t }\n\t function setAccessor(node, name, value, old, isSvg) {\n\t ensureNodeData(node)[name] = value;\n\t if ('key' !== name && 'children' !== name) if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {\n\t if (!value || isString(value) || isString(old)) node.style.cssText = value || '';\n\t if (value && 'object' == typeof value) {\n\t if (!isString(old)) for (var i in old) if (!(i in value)) node.style[i] = '';\n\t for (var i in value) node.style[i] = 'number' == typeof value[i] && !NON_DIMENSION_PROPS[i] ? value[i] + 'px' : value[i];\n\t }\n\t } else if ('dangerouslySetInnerHTML' === name) {\n\t if (value) node.innerHTML = value.__html;\n\t } else if ('o' === name[0] && 'n' === name[1]) {\n\t var l = node._listeners || (node._listeners = {});\n\t name = toLowerCase(name.substring(2));\n\t if (value) {\n\t if (!l[name]) node.addEventListener(name, eventProxy);\n\t } else if (l[name]) node.removeEventListener(name, eventProxy);\n\t l[name] = value;\n\t } else if ('type' !== name && !isSvg && name in node) {\n\t setProperty(node, name, empty(value) ? '' : value);\n\t if (falsey(value)) node.removeAttribute(name);\n\t } else {\n\t var ns = isSvg && name.match(/^xlink\\:?(.+)/);\n\t if (falsey(value)) if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1])); else node.removeAttribute(name); else if ('object' != typeof value && !isFunction(value)) if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), value); else node.setAttribute(name, value);\n\t }\n\t }\n\t function setProperty(node, name, value) {\n\t try {\n\t node[name] = value;\n\t } catch (e) {}\n\t }\n\t function eventProxy(e) {\n\t return this._listeners[e.type](options.event && options.event(e) || e);\n\t }\n\t function getRawNodeAttributes(node) {\n\t var attrs = {};\n\t for (var i = node.attributes.length; i--; ) attrs[node.attributes[i].name] = node.attributes[i].value;\n\t return attrs;\n\t }\n\t function isSameNodeType(node, vnode) {\n\t if (isString(vnode)) return 3 === getNodeType(node);\n\t if (isString(vnode.nodeName)) return isNamedNode(node, vnode.nodeName);\n\t if (isFunction(vnode.nodeName)) return node._componentConstructor === vnode.nodeName || isFunctionalComponent(vnode); else ;\n\t }\n\t function isNamedNode(node, nodeName) {\n\t return node.normalizedNodeName === nodeName || toLowerCase(node.nodeName) === toLowerCase(nodeName);\n\t }\n\t function getNodeProps(vnode) {\n\t var defaultProps = vnode.nodeName.defaultProps, props = clone(defaultProps || vnode.attributes);\n\t if (defaultProps) extend(props, vnode.attributes);\n\t if (vnode.children) props.children = vnode.children;\n\t return props;\n\t }\n\t function collectNode(node) {\n\t cleanNode(node);\n\t var name = toLowerCase(node.nodeName), list = nodes[name];\n\t if (list) list.push(node); else nodes[name] = [ node ];\n\t }\n\t function createNode(nodeName, isSvg) {\n\t var name = toLowerCase(nodeName), node = nodes[name] && nodes[name].pop() || (isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName));\n\t ensureNodeData(node);\n\t node.normalizedNodeName = name;\n\t return node;\n\t }\n\t function cleanNode(node) {\n\t removeNode(node);\n\t if (1 === getNodeType(node)) {\n\t ensureNodeData(node, getRawNodeAttributes(node));\n\t node._component = node._componentConstructor = null;\n\t }\n\t }\n\t function flushMounts() {\n\t var c;\n\t while (c = mounts.pop()) if (c.componentDidMount) c.componentDidMount();\n\t }\n\t function diff(dom, vnode, context, mountAll, parent) {\n\t diffLevel++;\n\t var ret = idiff(dom, vnode, context, mountAll);\n\t if (parent && ret.parentNode !== parent) parent.appendChild(ret);\n\t if (!--diffLevel) flushMounts();\n\t return ret;\n\t }\n\t function idiff(dom, vnode, context, mountAll) {\n\t var originalAttributes = vnode && vnode.attributes;\n\t while (isFunctionalComponent(vnode)) vnode = buildFunctionalComponent(vnode, context);\n\t if (empty(vnode)) return document.createComment('');\n\t if (isString(vnode)) {\n\t if (dom) {\n\t if (3 === getNodeType(dom) && dom.parentNode) {\n\t if (dom.nodeValue != vnode) dom.nodeValue = vnode;\n\t return dom;\n\t }\n\t collectNode(dom);\n\t }\n\t return document.createTextNode(vnode);\n\t }\n\t var svgMode, out = dom, nodeName = vnode.nodeName;\n\t if (isFunction(nodeName)) return buildComponentFromVNode(dom, vnode, context, mountAll);\n\t if (!isString(nodeName)) nodeName = String(nodeName);\n\t svgMode = 'svg' === toLowerCase(nodeName);\n\t if (svgMode) isSvgMode = !0;\n\t if (!dom) out = createNode(nodeName, isSvgMode); else if (!isNamedNode(dom, nodeName)) {\n\t out = createNode(nodeName, isSvgMode);\n\t while (dom.firstChild) out.appendChild(dom.firstChild);\n\t recollectNodeTree(dom);\n\t }\n\t if (vnode.children && 1 === vnode.children.length && 'string' == typeof vnode.children[0] && 1 === out.childNodes.length && out.firstChild instanceof Text) out.firstChild.nodeValue = vnode.children[0]; else if (vnode.children || out.firstChild) innerDiffNode(out, vnode.children, context, mountAll);\n\t diffAttributes(out, vnode.attributes);\n\t if (originalAttributes && originalAttributes.ref) (out[ATTR_KEY].ref = originalAttributes.ref)(out);\n\t if (svgMode) isSvgMode = !1;\n\t return out;\n\t }\n\t function innerDiffNode(dom, vchildren, context, mountAll) {\n\t var j, c, vchild, child, originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren && vchildren.length;\n\t if (len) for (var i = 0; i < len; i++) {\n\t var _child = originalChildren[i], key = vlen ? (c = _child._component) ? c.__key : (c = _child[ATTR_KEY]) ? c.key : null : null;\n\t if (key || 0 === key) {\n\t keyedLen++;\n\t keyed[key] = _child;\n\t } else children[childrenLen++] = _child;\n\t }\n\t if (vlen) for (var i = 0; i < vlen; i++) {\n\t vchild = vchildren[i];\n\t child = null;\n\t if (keyedLen && vchild.attributes) {\n\t var key = vchild.key;\n\t if (!empty(key) && key in keyed) {\n\t child = keyed[key];\n\t keyed[key] = void 0;\n\t keyedLen--;\n\t }\n\t }\n\t if (!child && min < childrenLen) for (j = min; j < childrenLen; j++) {\n\t c = children[j];\n\t if (c && isSameNodeType(c, vchild)) {\n\t child = c;\n\t children[j] = void 0;\n\t if (j === childrenLen - 1) childrenLen--;\n\t if (j === min) min++;\n\t break;\n\t }\n\t }\n\t child = idiff(child, vchild, context, mountAll);\n\t if (child !== originalChildren[i]) dom.insertBefore(child, originalChildren[i] || null);\n\t }\n\t if (keyedLen) for (var i in keyed) if (keyed[i]) children[min = childrenLen++] = keyed[i];\n\t if (min < childrenLen) removeOrphanedChildren(children);\n\t }\n\t function removeOrphanedChildren(children, unmountOnly) {\n\t for (var i = children.length; i--; ) {\n\t var child = children[i];\n\t if (child) recollectNodeTree(child, unmountOnly);\n\t }\n\t }\n\t function recollectNodeTree(node, unmountOnly) {\n\t var component = node._component;\n\t if (component) unmountComponent(component, !unmountOnly); else {\n\t if (node[ATTR_KEY] && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);\n\t if (!unmountOnly) collectNode(node);\n\t if (node.childNodes && node.childNodes.length) removeOrphanedChildren(node.childNodes, unmountOnly);\n\t }\n\t }\n\t function diffAttributes(dom, attrs) {\n\t var old = dom[ATTR_KEY] || getRawNodeAttributes(dom);\n\t for (var _name in old) if (!(attrs && _name in attrs)) setAccessor(dom, _name, null, old[_name], isSvgMode);\n\t if (attrs) for (var _name2 in attrs) if (!(_name2 in old) || attrs[_name2] != ('value' === _name2 || 'selected' === _name2 || 'checked' === _name2 ? dom[_name2] : old[_name2])) setAccessor(dom, _name2, attrs[_name2], old[_name2], isSvgMode);\n\t }\n\t function collectComponent(component) {\n\t var name = component.constructor.name, list = components[name];\n\t if (list) list.push(component); else components[name] = [ component ];\n\t }\n\t function createComponent(Ctor, props, context) {\n\t var inst = new Ctor(props, context), list = components[Ctor.name];\n\t if (list) for (var i = list.length; i--; ) if (list[i].constructor === Ctor) {\n\t inst.nextBase = list[i].nextBase;\n\t list.splice(i, 1);\n\t break;\n\t }\n\t return inst;\n\t }\n\t function triggerComponentRender(component) {\n\t if (!component._dirty) {\n\t component._dirty = !0;\n\t enqueueRender(component);\n\t }\n\t }\n\t function setComponentProps(component, props, opts, context, mountAll) {\n\t var b = component.base;\n\t if (!component._disableRendering) {\n\t component._disableRendering = !0;\n\t if (component.__ref = props.ref) delete props.ref;\n\t if (component.__key = props.key) delete props.key;\n\t if (empty(b) || mountAll) {\n\t if (component.componentWillMount) component.componentWillMount();\n\t } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);\n\t if (context && context !== component.context) {\n\t if (!component.prevContext) component.prevContext = component.context;\n\t component.context = context;\n\t }\n\t if (!component.prevProps) component.prevProps = component.props;\n\t component.props = props;\n\t component._disableRendering = !1;\n\t if (0 !== opts) if (1 === opts || options.syncComponentUpdates !== !1 || !b) renderComponent(component, 1, mountAll); else triggerComponentRender(component);\n\t if (component.__ref) component.__ref(component);\n\t }\n\t }\n\t function renderComponent(component, opts, mountAll) {\n\t if (!component._disableRendering) {\n\t var skip, rendered, props = component.props, state = component.state, context = component.context, previousProps = component.prevProps || props, previousState = component.prevState || state, previousContext = component.prevContext || context, isUpdate = component.base, initialBase = isUpdate || component.nextBase, nextSibling = initialBase && initialBase.nextSibling, baseParent = initialBase && initialBase.parentNode, initialComponent = initialBase && initialBase._component, initialChildComponent = component._component;\n\t if (isUpdate) {\n\t component.props = previousProps;\n\t component.state = previousState;\n\t component.context = previousContext;\n\t if (2 !== opts && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === !1) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);\n\t component.props = props;\n\t component.state = state;\n\t component.context = context;\n\t }\n\t component.prevProps = component.prevState = component.prevContext = component.nextBase = null;\n\t component._dirty = !1;\n\t if (!skip) {\n\t if (component.render) rendered = component.render(props, state, context);\n\t if (component.getChildContext) context = extend(clone(context), component.getChildContext());\n\t while (isFunctionalComponent(rendered)) rendered = buildFunctionalComponent(rendered, context);\n\t var toUnmount, base, childComponent = rendered && rendered.nodeName;\n\t if (isFunction(childComponent) && childComponent.prototype.render) {\n\t var inst = initialChildComponent, childProps = getNodeProps(rendered);\n\t if (inst && inst.constructor === childComponent) setComponentProps(inst, childProps, 1, context); else {\n\t toUnmount = inst;\n\t inst = createComponent(childComponent, childProps, context);\n\t inst._parentComponent = component;\n\t component._component = inst;\n\t setComponentProps(inst, childProps, 0, context);\n\t renderComponent(inst, 1);\n\t }\n\t base = inst.base;\n\t } else {\n\t var cbase = initialBase;\n\t toUnmount = initialChildComponent;\n\t if (toUnmount) cbase = component._component = null;\n\t if (initialBase || 1 === opts) {\n\t if (cbase) cbase._component = null;\n\t base = diff(cbase, rendered, context, mountAll || !isUpdate);\n\t }\n\t }\n\t if (initialBase && base !== initialBase) {\n\t if (baseParent && base !== baseParent) baseParent.insertBefore(base, nextSibling || null);\n\t if (!toUnmount && initialComponent === component && !initialChildComponent && initialBase.parentNode) {\n\t initialBase._component = null;\n\t recollectNodeTree(initialBase);\n\t }\n\t }\n\t if (toUnmount) unmountComponent(toUnmount, !0);\n\t component.base = base;\n\t if (base) {\n\t var componentRef = component, t = component;\n\t while (t = t._parentComponent) componentRef = t;\n\t base._component = componentRef;\n\t base._componentConstructor = componentRef.constructor;\n\t }\n\t }\n\t if (!isUpdate || mountAll) {\n\t mounts.unshift(component);\n\t if (!diffLevel) flushMounts();\n\t } else if (!skip && component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, previousContext);\n\t var fn, cb = component._renderCallbacks;\n\t if (cb) while (fn = cb.pop()) fn.call(component);\n\t return rendered;\n\t }\n\t }\n\t function buildComponentFromVNode(dom, vnode, context, mountAll) {\n\t var c = dom && dom._component, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);\n\t while (c && !isOwner && (c = c._parentComponent)) isOwner = c.constructor === vnode.nodeName;\n\t if (isOwner && (!mountAll || c._component)) {\n\t setComponentProps(c, props, 3, context, mountAll);\n\t dom = c.base;\n\t } else {\n\t if (c && !isDirectOwner) {\n\t unmountComponent(c, !0);\n\t dom = oldDom = null;\n\t }\n\t c = createComponent(vnode.nodeName, props, context);\n\t if (dom && !c.nextBase) c.nextBase = dom;\n\t setComponentProps(c, props, 1, context, mountAll);\n\t dom = c.base;\n\t if (oldDom && dom !== oldDom) {\n\t oldDom._component = null;\n\t recollectNodeTree(oldDom);\n\t }\n\t }\n\t return dom;\n\t }\n\t function unmountComponent(component, remove) {\n\t var base = component.base;\n\t component._disableRendering = !0;\n\t if (component.componentWillUnmount) component.componentWillUnmount();\n\t component.base = null;\n\t var inner = component._component;\n\t if (inner) unmountComponent(inner, remove); else if (base) {\n\t if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null);\n\t component.nextBase = base;\n\t if (remove) {\n\t removeNode(base);\n\t collectComponent(component);\n\t }\n\t removeOrphanedChildren(base.childNodes, !remove);\n\t }\n\t if (component.__ref) component.__ref(null);\n\t if (component.componentDidUnmount) component.componentDidUnmount();\n\t }\n\t function Component(props, context) {\n\t this._dirty = !0;\n\t this._disableRendering = !1;\n\t this.prevState = this.prevProps = this.prevContext = this.base = this.nextBase = this._parentComponent = this._component = this.__ref = this.__key = this._linkedStates = this._renderCallbacks = null;\n\t this.context = context || {};\n\t this.props = props;\n\t this.state = this.getInitialState && this.getInitialState() || {};\n\t }\n\t function render(vnode, parent, merge) {\n\t return diff(merge, vnode, {}, !1, parent);\n\t }\n\t var lcCache = {};\n\t var toLowerCase = function(s) {\n\t return lcCache[s] || (lcCache[s] = s.toLowerCase());\n\t };\n\t var resolved = 'undefined' != typeof Promise && Promise.resolve();\n\t var setImmediate = resolved ? function(f) {\n\t resolved.then(f);\n\t } : setTimeout;\n\t var options = {\n\t vnode: empty\n\t };\n\t var SHARED_TEMP_ARRAY = [];\n\t var EMPTY = {};\n\t var ATTR_KEY = 'undefined' != typeof Symbol ? Symbol.for('preactattr') : '__preactattr_';\n\t var NON_DIMENSION_PROPS = {\n\t boxFlex: 1,\n\t boxFlexGroup: 1,\n\t columnCount: 1,\n\t fillOpacity: 1,\n\t flex: 1,\n\t flexGrow: 1,\n\t flexPositive: 1,\n\t flexShrink: 1,\n\t flexNegative: 1,\n\t fontWeight: 1,\n\t lineClamp: 1,\n\t lineHeight: 1,\n\t opacity: 1,\n\t order: 1,\n\t orphans: 1,\n\t strokeOpacity: 1,\n\t widows: 1,\n\t zIndex: 1,\n\t zoom: 1\n\t };\n\t var items = [];\n\t var itemsOffline = [];\n\t var nodes = {};\n\t var mounts = [];\n\t var diffLevel = 0;\n\t var isSvgMode = !1;\n\t var components = {};\n\t extend(Component.prototype, {\n\t linkState: function(key, eventPath) {\n\t var c = this._linkedStates || (this._linkedStates = {}), cacheKey = key + '|' + eventPath;\n\t return c[cacheKey] || (c[cacheKey] = createLinkedState(this, key, eventPath));\n\t },\n\t setState: function(state, callback) {\n\t var s = this.state;\n\t if (!this.prevState) this.prevState = clone(s);\n\t extend(s, isFunction(state) ? state(s, this.props) : state);\n\t if (callback) (this._renderCallbacks = this._renderCallbacks || []).push(callback);\n\t triggerComponentRender(this);\n\t },\n\t forceUpdate: function() {\n\t renderComponent(this, 2);\n\t },\n\t render: function() {\n\t return null;\n\t }\n\t });\n\t exports.h = h;\n\t exports.cloneElement = cloneElement;\n\t exports.Component = Component;\n\t exports.render = render;\n\t exports.rerender = rerender;\n\t exports.options = options;\n\t});\n\t//# sourceMappingURL=preact.js.map\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2).setImmediate))\n\n/***/ },\n/* 2 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(setImmediate, clearImmediate) {var nextTick = __webpack_require__(3).nextTick;\n\tvar apply = Function.prototype.apply;\n\tvar slice = Array.prototype.slice;\n\tvar immediateIds = {};\n\tvar nextImmediateId = 0;\n\t\n\t// DOM APIs, for completeness\n\t\n\texports.setTimeout = function() {\n\t return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);\n\t};\n\texports.setInterval = function() {\n\t return new Timeout(apply.call(setInterval, window, arguments), clearInterval);\n\t};\n\texports.clearTimeout =\n\texports.clearInterval = function(timeout) { timeout.close(); };\n\t\n\tfunction Timeout(id, clearFn) {\n\t this._id = id;\n\t this._clearFn = clearFn;\n\t}\n\tTimeout.prototype.unref = Timeout.prototype.ref = function() {};\n\tTimeout.prototype.close = function() {\n\t this._clearFn.call(window, this._id);\n\t};\n\t\n\t// Does not start the time, just sets up the members needed.\n\texports.enroll = function(item, msecs) {\n\t clearTimeout(item._idleTimeoutId);\n\t item._idleTimeout = msecs;\n\t};\n\t\n\texports.unenroll = function(item) {\n\t clearTimeout(item._idleTimeoutId);\n\t item._idleTimeout = -1;\n\t};\n\t\n\texports._unrefActive = exports.active = function(item) {\n\t clearTimeout(item._idleTimeoutId);\n\t\n\t var msecs = item._idleTimeout;\n\t if (msecs >= 0) {\n\t item._idleTimeoutId = setTimeout(function onTimeout() {\n\t if (item._onTimeout)\n\t item._onTimeout();\n\t }, msecs);\n\t }\n\t};\n\t\n\t// That's not how node.js implements it but the exposed api is the same.\n\texports.setImmediate = typeof setImmediate === \"function\" ? setImmediate : function(fn) {\n\t var id = nextImmediateId++;\n\t var args = arguments.length < 2 ? false : slice.call(arguments, 1);\n\t\n\t immediateIds[id] = true;\n\t\n\t nextTick(function onNextTick() {\n\t if (immediateIds[id]) {\n\t // fn.call() is faster so we optimize for the common use-case\n\t // @see http://jsperf.com/call-apply-segu\n\t if (args) {\n\t fn.apply(null, args);\n\t } else {\n\t fn.call(null);\n\t }\n\t // Prevent ids from leaking\n\t exports.clearImmediate(id);\n\t }\n\t });\n\t\n\t return id;\n\t};\n\t\n\texports.clearImmediate = typeof clearImmediate === \"function\" ? clearImmediate : function(id) {\n\t delete immediateIds[id];\n\t};\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2).setImmediate, __webpack_require__(2).clearImmediate))\n\n/***/ },\n/* 3 */\n/***/ function(module, exports) {\n\n\t// shim for using process in browser\n\tvar process = module.exports = {};\n\t\n\t// cached from whatever global is present so that test runners that stub it\n\t// don't break things. But we need to wrap it in a try catch in case it is\n\t// wrapped in strict mode code which doesn't define any globals. It's inside a\n\t// function because try/catches deoptimize in certain engines.\n\t\n\tvar cachedSetTimeout;\n\tvar cachedClearTimeout;\n\t\n\t(function () {\n\t try {\n\t cachedSetTimeout = setTimeout;\n\t } catch (e) {\n\t cachedSetTimeout = function () {\n\t throw new Error('setTimeout is not defined');\n\t }\n\t }\n\t try {\n\t cachedClearTimeout = clearTimeout;\n\t } catch (e) {\n\t cachedClearTimeout = function () {\n\t throw new Error('clearTimeout is not defined');\n\t }\n\t }\n\t} ())\n\tfunction runTimeout(fun) {\n\t if (cachedSetTimeout === setTimeout) {\n\t //normal enviroments in sane situations\n\t return setTimeout(fun, 0);\n\t }\n\t try {\n\t // when when somebody has screwed with setTimeout but no I.E. maddness\n\t return cachedSetTimeout(fun, 0);\n\t } catch(e){\n\t try {\n\t // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n\t return cachedSetTimeout.call(null, fun, 0);\n\t } catch(e){\n\t // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n\t return cachedSetTimeout.call(this, fun, 0);\n\t }\n\t }\n\t\n\t\n\t}\n\tfunction runClearTimeout(marker) {\n\t if (cachedClearTimeout === clearTimeout) {\n\t //normal enviroments in sane situations\n\t return clearTimeout(marker);\n\t }\n\t try {\n\t // when when somebody has screwed with setTimeout but no I.E. maddness\n\t return cachedClearTimeout(marker);\n\t } catch (e){\n\t try {\n\t // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n\t return cachedClearTimeout.call(null, marker);\n\t } catch (e){\n\t // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n\t // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n\t return cachedClearTimeout.call(this, marker);\n\t }\n\t }\n\t\n\t\n\t\n\t}\n\tvar queue = [];\n\tvar draining = false;\n\tvar currentQueue;\n\tvar queueIndex = -1;\n\t\n\tfunction cleanUpNextTick() {\n\t if (!draining || !currentQueue) {\n\t return;\n\t }\n\t draining = false;\n\t if (currentQueue.length) {\n\t queue = currentQueue.concat(queue);\n\t } else {\n\t queueIndex = -1;\n\t }\n\t if (queue.length) {\n\t drainQueue();\n\t }\n\t}\n\t\n\tfunction drainQueue() {\n\t if (draining) {\n\t return;\n\t }\n\t var timeout = runTimeout(cleanUpNextTick);\n\t draining = true;\n\t\n\t var len = queue.length;\n\t while(len) {\n\t currentQueue = queue;\n\t queue = [];\n\t while (++queueIndex < len) {\n\t if (currentQueue) {\n\t currentQueue[queueIndex].run();\n\t }\n\t }\n\t queueIndex = -1;\n\t len = queue.length;\n\t }\n\t currentQueue = null;\n\t draining = false;\n\t runClearTimeout(timeout);\n\t}\n\t\n\tprocess.nextTick = function (fun) {\n\t var args = new Array(arguments.length - 1);\n\t if (arguments.length > 1) {\n\t for (var i = 1; i < arguments.length; i++) {\n\t args[i - 1] = arguments[i];\n\t }\n\t }\n\t queue.push(new Item(fun, args));\n\t if (queue.length === 1 && !draining) {\n\t runTimeout(drainQueue);\n\t }\n\t};\n\t\n\t// v8 likes predictible objects\n\tfunction Item(fun, array) {\n\t this.fun = fun;\n\t this.array = array;\n\t}\n\tItem.prototype.run = function () {\n\t this.fun.apply(null, this.array);\n\t};\n\tprocess.title = 'browser';\n\tprocess.browser = true;\n\tprocess.env = {};\n\tprocess.argv = [];\n\tprocess.version = ''; // empty string to avoid regexp issues\n\tprocess.versions = {};\n\t\n\tfunction noop() {}\n\t\n\tprocess.on = noop;\n\tprocess.addListener = noop;\n\tprocess.once = noop;\n\tprocess.off = noop;\n\tprocess.removeListener = noop;\n\tprocess.removeAllListeners = noop;\n\tprocess.emit = noop;\n\t\n\tprocess.binding = function (name) {\n\t throw new Error('process.binding is not supported');\n\t};\n\t\n\tprocess.cwd = function () { return '/' };\n\tprocess.chdir = function (dir) {\n\t throw new Error('process.chdir is not supported');\n\t};\n\tprocess.umask = function() { return 0; };\n\n\n/***/ },\n/* 4 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\t\n\tvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol ? \"symbol\" : typeof obj; };\n\t\n\tvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\t\n\tvar _preact = __webpack_require__(1);\n\t\n\tvar _objectUtils = __webpack_require__(5);\n\t\n\tvar _arrayUtils = __webpack_require__(7);\n\t\n\tvar _typeUtils = __webpack_require__(6);\n\t\n\tvar _bindMethods = __webpack_require__(8);\n\t\n\tvar _bindMethods2 = _interopRequireDefault(_bindMethods);\n\t\n\tvar _JSONNode = __webpack_require__(9);\n\t\n\tvar _JSONNode2 = _interopRequireDefault(_JSONNode);\n\t\n\tfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\t\n\tfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\t\n\tfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\t\n\tfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\t\n\tvar TreeMode = function (_Component) {\n\t _inherits(TreeMode, _Component);\n\t\n\t // TODO: define propTypes\n\t\n\t function TreeMode(props) {\n\t _classCallCheck(this, TreeMode);\n\t\n\t var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TreeMode).call(this, props));\n\t\n\t (0, _bindMethods2.default)(_this);\n\t\n\t var name = _this.props.options && _this.props.options.name || null;\n\t var expand = _this.props.options && _this.props.options.expand || TreeMode.expand;\n\t\n\t _this.state = {\n\t options: {\n\t name: name\n\t },\n\t\n\t data: jsonToData([], _this.props.data || {}, expand),\n\t\n\t events: {\n\t onChangeProperty: _this.handleChangeProperty,\n\t onChangeValue: _this.handleChangeValue,\n\t onChangeType: _this.handleChangeType,\n\t onInsert: _this.handleInsert,\n\t onAppend: _this.handleAppend,\n\t onDuplicate: _this.handleDuplicate,\n\t onRemove: _this.handleRemove,\n\t onSort: _this.handleSort,\n\t\n\t onExpand: _this.handleExpand\n\t },\n\t\n\t search: null\n\t };\n\t return _this;\n\t }\n\t\n\t _createClass(TreeMode, [{\n\t key: 'render',\n\t value: function render() {\n\t return (0, _preact.h)('div', { class: 'jsoneditor', contentEditable: 'false', onClick: _JSONNode2.default.hideContextMenu }, [(0, _preact.h)('ul', { class: 'jsoneditor-list', contentEditable: 'false' }, [(0, _preact.h)(_JSONNode2.default, {\n\t data: this.state.data,\n\t events: this.state.events,\n\t options: this.state.options,\n\t parent: null,\n\t prop: null\n\t })])]);\n\t }\n\t }, {\n\t key: 'handleChangeValue',\n\t value: function handleChangeValue(path, value) {\n\t console.log('handleChangeValue', path, value);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t\n\t this.setState({\n\t data: (0, _objectUtils.setIn)(this.state.data, dataPath.concat(['value']), value)\n\t });\n\t }\n\t }, {\n\t key: 'handleChangeProperty',\n\t value: function handleChangeProperty(path, oldProp, newProp) {\n\t console.log('handleChangeProperty', path, oldProp, newProp);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t var object = (0, _objectUtils.getIn)(this.state.data, dataPath);\n\t var index = object.props.findIndex(function (p) {\n\t return p.name === oldProp;\n\t });\n\t\n\t this.setState({\n\t data: (0, _objectUtils.setIn)(this.state.data, dataPath.concat(['props', index, 'name']), newProp)\n\t });\n\t }\n\t }, {\n\t key: 'handleChangeType',\n\t value: function handleChangeType(path, type) {\n\t console.log('handleChangeType', path, type);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t var oldEntry = (0, _objectUtils.getIn)(this.state.data, dataPath);\n\t var newEntry = convertDataEntry(oldEntry, type);\n\t\n\t this.setState({\n\t data: (0, _objectUtils.setIn)(this.state.data, dataPath, newEntry)\n\t });\n\t }\n\t }, {\n\t key: 'handleInsert',\n\t value: function handleInsert(path, afterProp, type) {\n\t console.log('handleInsert', path, afterProp, type);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t var parent = (0, _objectUtils.getIn)(this.state.data, dataPath);\n\t\n\t if (parent.type === 'array') {\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['items']), function (items) {\n\t var index = parseInt(afterProp);\n\t var updatedItems = items.slice(0);\n\t\n\t updatedItems.splice(index + 1, 0, createDataEntry(type));\n\t\n\t return updatedItems;\n\t })\n\t });\n\t } else {\n\t // parent.type === 'object'\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['props']), function (props) {\n\t var index = props.findIndex(function (p) {\n\t return p.name === afterProp;\n\t });\n\t var updatedProps = props.slice(0);\n\t\n\t updatedProps.splice(index + 1, 0, {\n\t name: '',\n\t value: createDataEntry(type)\n\t });\n\t\n\t return updatedProps;\n\t })\n\t });\n\t }\n\t }\n\t }, {\n\t key: 'handleAppend',\n\t value: function handleAppend(path, type) {\n\t console.log('handleAppend', path, type);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t var object = (0, _objectUtils.getIn)(this.state.data, dataPath);\n\t\n\t if (object.type === 'array') {\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['items']), function (items) {\n\t var updatedItems = items.slice(0);\n\t\n\t updatedItems.push(createDataEntry(type));\n\t\n\t return updatedItems;\n\t })\n\t });\n\t } else {\n\t // object.type === 'object'\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['props']), function (props) {\n\t var updatedProps = props.slice(0);\n\t\n\t updatedProps.push({\n\t name: '',\n\t value: createDataEntry(type)\n\t });\n\t\n\t return updatedProps;\n\t })\n\t });\n\t }\n\t }\n\t }, {\n\t key: 'handleDuplicate',\n\t value: function handleDuplicate(path, prop) {\n\t console.log('handleDuplicate', path);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t var object = (0, _objectUtils.getIn)(this.state.data, dataPath);\n\t\n\t if (object.type === 'array') {\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['items']), function (items) {\n\t var index = parseInt(prop);\n\t var updatedItems = items.slice(0);\n\t var original = items[index];\n\t var duplicate = (0, _objectUtils.cloneDeep)(original);\n\t\n\t updatedItems.splice(index + 1, 0, duplicate);\n\t\n\t return updatedItems;\n\t })\n\t });\n\t } else {\n\t // object.type === 'object'\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['props']), function (props) {\n\t var index = props.findIndex(function (p) {\n\t return p.name === prop;\n\t });\n\t var updated = props.slice(0);\n\t var original = props[index];\n\t var duplicate = (0, _objectUtils.cloneDeep)(original);\n\t\n\t updated.splice(index + 1, 0, duplicate);\n\t\n\t return updated;\n\t })\n\t });\n\t }\n\t }\n\t }, {\n\t key: 'handleRemove',\n\t value: function handleRemove(path, prop) {\n\t console.log('handleRemove', path);\n\t\n\t var object = (0, _objectUtils.getIn)(this.state.data, toDataPath(this.state.data, path));\n\t\n\t if (object.type === 'array') {\n\t var dataPath = toDataPath(this.state.data, path.concat(prop));\n\t\n\t this.setState({\n\t data: (0, _objectUtils.deleteIn)(this.state.data, dataPath)\n\t });\n\t } else {\n\t // object.type === 'object'\n\t var _dataPath = toDataPath(this.state.data, path.concat(prop));\n\t\n\t _dataPath.pop(); // remove the 'value' property, we want to remove the whole object property\n\t this.setState({\n\t data: (0, _objectUtils.deleteIn)(this.state.data, _dataPath)\n\t });\n\t }\n\t }\n\t\n\t /**\n\t * Order the items of an array or the properties of an object in ascending\n\t * or descending order\n\t * @param {Array.} path\n\t * @param {'asc' | 'desc' | null} [order=null] If not provided, will toggle current ordering\n\t */\n\t\n\t }, {\n\t key: 'handleSort',\n\t value: function handleSort(path) {\n\t var order = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];\n\t\n\t console.log('handleSort', path, order);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t var object = (0, _objectUtils.getIn)(this.state.data, dataPath);\n\t\n\t var _order = void 0;\n\t if (order === 'asc' || order === 'desc') {\n\t _order = order;\n\t } else {\n\t // toggle previous order\n\t _order = object.order !== 'asc' ? 'asc' : 'desc';\n\t\n\t this.setState({\n\t data: (0, _objectUtils.setIn)(this.state.data, dataPath.concat(['order']), _order)\n\t });\n\t }\n\t\n\t if (object.type === 'array') {\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['items']), function (items) {\n\t var ordered = items.slice(0);\n\t var compare = _order === 'desc' ? _arrayUtils.compareDesc : _arrayUtils.compareAsc;\n\t\n\t ordered.sort(function (a, b) {\n\t return compare(a.value, b.value);\n\t });\n\t\n\t return ordered;\n\t })\n\t });\n\t } else {\n\t // object.type === 'object'\n\t this.setState({\n\t data: (0, _objectUtils.updateIn)(this.state.data, dataPath.concat(['props']), function (props) {\n\t var orderedProps = props.slice(0);\n\t var compare = _order === 'desc' ? _arrayUtils.compareDesc : _arrayUtils.compareAsc;\n\t\n\t orderedProps.sort(function (a, b) {\n\t return compare(a.name, b.name);\n\t });\n\t\n\t return orderedProps;\n\t })\n\t });\n\t }\n\t }\n\t }, {\n\t key: 'handleExpand',\n\t value: function handleExpand(path, expand) {\n\t console.log('handleExpand', path, expand);\n\t\n\t var dataPath = toDataPath(this.state.data, path);\n\t\n\t this.setState({\n\t data: (0, _objectUtils.setIn)(this.state.data, dataPath.concat(['expanded']), expand)\n\t });\n\t }\n\t\n\t /**\n\t * Set JSON object in editor\n\t * @param {Object | Array | string | number | boolean | null} json JSON data\n\t * @param {SetOptions} [options]\n\t */\n\t\n\t }, {\n\t key: 'set',\n\t value: function set(json) {\n\t var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];\n\t\n\t this.setState({\n\t options: (0, _objectUtils.setIn)(this.state.options, ['name'], options && options.name || null),\n\t\n\t data: jsonToData([], json, options.expand || TreeMode.expand)\n\t });\n\t }\n\t\n\t /**\n\t * Get JSON from the editor\n\t * @returns {Object | Array | string | number | boolean | null} json\n\t */\n\t\n\t }, {\n\t key: 'get',\n\t value: function get() {\n\t return dataToJson(this.state.data);\n\t }\n\t\n\t // TODO: implement expand\n\t // TODO: implement getText and setText\n\t\n\t /**\n\t * Default function to determine whether or not to expand a node initially\n\t *\n\t * Rule: expand the root node only\n\t *\n\t * @param {Array.} path\n\t * @return {boolean}\n\t */\n\t\n\t }], [{\n\t key: 'expand',\n\t value: function expand(path) {\n\t return path.length === 0;\n\t }\n\t }]);\n\t\n\t return TreeMode;\n\t}(_preact.Component);\n\t\n\t/**\n\t * Convert a path of a JSON object into a path in the corresponding data model\n\t * @param {Data} data\n\t * @param {Array.} path\n\t * @return {Array.} dataPath\n\t * @private\n\t */\n\t\n\t\n\texports.default = TreeMode;\n\tfunction toDataPath(data, path) {\n\t if (path.length === 0) {\n\t return [];\n\t }\n\t\n\t var index = void 0;\n\t if (data.type === 'array') {\n\t // index of an array\n\t index = path[0];\n\t\n\t return ['items', index].concat(toDataPath(data.items[index], path.slice(1)));\n\t } else {\n\t // object property. find the index of this property\n\t index = data.props.findIndex(function (prop) {\n\t return prop.name === path[0];\n\t });\n\t\n\t return ['props', index, 'value'].concat(toDataPath(data.props[index].value, path.slice(1)));\n\t }\n\t}\n\t\n\t/**\n\t * Convert a JSON object into the internally used data model\n\t * @param {Array.} path\n\t * @param {Object | Array | string | number | boolean | null} json\n\t * @param {function(path: Array.)} expand\n\t * @return {Data}\n\t */\n\tfunction jsonToData(path, json, expand) {\n\t if (Array.isArray(json)) {\n\t return {\n\t type: 'array',\n\t expanded: expand(path),\n\t items: json.map(function (child, index) {\n\t return jsonToData(path.concat(index), child, expand);\n\t })\n\t };\n\t } else if ((0, _typeUtils.isObject)(json)) {\n\t return {\n\t type: 'object',\n\t expanded: expand(path),\n\t props: Object.keys(json).map(function (name) {\n\t return {\n\t name: name,\n\t value: jsonToData(path.concat(name), json[name], expand)\n\t };\n\t })\n\t };\n\t } else {\n\t return {\n\t type: 'value',\n\t value: json\n\t };\n\t }\n\t}\n\t\n\t/**\n\t * Convert the internal data model to a regular JSON object\n\t * @param {Data} data\n\t * @return {Object | Array | string | number | boolean | null} json\n\t */\n\tfunction dataToJson(data) {\n\t var _ret = function () {\n\t switch (data.type) {\n\t case 'array':\n\t return {\n\t v: data.items.map(dataToJson)\n\t };\n\t\n\t case 'object':\n\t var object = {};\n\t\n\t data.props.forEach(function (prop) {\n\t object[prop.name] = dataToJson(prop.value);\n\t });\n\t\n\t return {\n\t v: object\n\t };\n\t\n\t default:\n\t // type 'string' or 'value'\n\t return {\n\t v: data.value\n\t };\n\t }\n\t }();\n\t\n\t if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === \"object\") return _ret.v;\n\t}\n\t\n\t/**\n\t * Create a new data entry\n\t * @param {'object' | 'array' | 'value' | 'string'} [type='value']\n\t * @return {*}\n\t */\n\tfunction createDataEntry(type) {\n\t if (type === 'array') {\n\t return {\n\t type: type,\n\t expanded: true,\n\t items: []\n\t };\n\t } else if (type === 'object') {\n\t return {\n\t type: type,\n\t expanded: true,\n\t props: []\n\t };\n\t } else {\n\t return {\n\t type: type,\n\t value: ''\n\t };\n\t }\n\t}\n\t\n\t/**\n\t * Convert an entry into a different type. When possible, data is retained\n\t * @param {Data} entry\n\t * @param {'object' | 'array' | 'value' | 'string'} type\n\t */\n\tfunction convertDataEntry(entry, type) {\n\t var convertedEntry = createDataEntry(type);\n\t\n\t // convert contents from old value to new value where possible\n\t if (type === 'value' && entry.type === 'string') {\n\t convertedEntry.value = (0, _typeUtils.stringConvert)(entry.value);\n\t }\n\t\n\t if (type === 'string' && entry.type === 'value') {\n\t convertedEntry.value = entry.value + '';\n\t }\n\t\n\t if (type === 'object' && entry.type === 'array') {\n\t convertedEntry.props = entry.items.map(function (item, index) {\n\t return {\n\t name: index + '',\n\t value: item\n\t };\n\t });\n\t }\n\t\n\t if (type === 'array' && entry.type === 'object') {\n\t convertedEntry.items = entry.props.map(function (prop) {\n\t return prop.value;\n\t });\n\t }\n\t\n\t return convertedEntry;\n\t}\n\n/***/ },\n/* 5 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\t\n\tvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol ? \"symbol\" : typeof obj; };\n\t\n\texports.clone = clone;\n\texports.cloneDeep = cloneDeep;\n\texports.getIn = getIn;\n\texports.setIn = setIn;\n\texports.updateIn = updateIn;\n\texports.deleteIn = deleteIn;\n\t\n\tvar _typeUtils = __webpack_require__(6);\n\t\n\t// inspiration:\n\t//\n\t// https://www.npmjs.com/package/seamless-immutable\n\t// https://www.npmjs.com/package/ih\n\t// https://www.npmjs.com/package/mutatis\n\t\n\t// TODO: unit test clone\n\t\n\t/**\n\t * Flat clone the properties of an object or array\n\t * @param {Object | Array} value\n\t * @return {Object | Array} Returns a flat clone of the object or Array\n\t */\n\tfunction clone(value) {\n\t if (Array.isArray(value)) {\n\t return value.slice(0);\n\t } else if ((0, _typeUtils.isObject)(value)) {\n\t var _ret = function () {\n\t var cloned = {};\n\t\n\t Object.keys(value).forEach(function (key) {\n\t cloned[key] = value[key];\n\t });\n\t\n\t return {\n\t v: cloned\n\t };\n\t }();\n\t\n\t if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === \"object\") return _ret.v;\n\t } else {\n\t // a primitive value\n\t return value;\n\t }\n\t}\n\t\n\t// TODO: test cloneDeep\n\t\n\t/**\n\t * Deep clone the properties of an object or array\n\t * @param {Object | Array} value\n\t * @return {Object | Array} Returns a deep clone of the object or Array\n\t */\n\tfunction cloneDeep(value) {\n\t if (Array.isArray(value)) {\n\t return value.map(cloneDeep);\n\t } else if ((0, _typeUtils.isObject)(value)) {\n\t var _ret2 = function () {\n\t var cloned = {};\n\t\n\t Object.keys(value).forEach(function (key) {\n\t cloned[key] = cloneDeep(value[key]);\n\t });\n\t\n\t return {\n\t v: cloned\n\t };\n\t }();\n\t\n\t if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === \"object\") return _ret2.v;\n\t } else {\n\t // a primitive value\n\t return value;\n\t }\n\t}\n\t\n\t// TODO: unit test getIn\n\t\n\t/**\n\t * helper function to get a nested property in an object or array\n\t *\n\t * @param {Object | Array} object\n\t * @param {Array.} path\n\t * @return {* | undefined} Returns the field when found, or undefined when the\n\t * path doesn't exist\n\t */\n\tfunction getIn(object, path) {\n\t var value = object;\n\t var i = 0;\n\t\n\t while (i < path.length) {\n\t if (Array.isArray(value) || (0, _typeUtils.isObject)(value)) {\n\t value = value[path[i]];\n\t } else {\n\t value = undefined;\n\t }\n\t\n\t i++;\n\t }\n\t\n\t return value;\n\t}\n\t\n\t// TODO: unit test setIn\n\t\n\t/**\n\t * helper function to replace a nested property in an object with a new value\n\t * without mutating the object itself.\n\t *\n\t * @param {Object | Array} object\n\t * @param {Array.} path\n\t * @param {*} value\n\t * @return {Object | Array} Returns a new, updated object or array\n\t */\n\tfunction setIn(object, path, value) {\n\t if (path.length === 0) {\n\t return value;\n\t }\n\t\n\t var key = path[0];\n\t var updated = void 0;\n\t if (typeof key === 'string' && !(0, _typeUtils.isObject)(object)) {\n\t updated = {}; // change into an object\n\t } else if (typeof key === 'number' && !Array.isArray(object)) {\n\t updated = []; // change into an array\n\t } else {\n\t updated = clone(object);\n\t }\n\t\n\t updated[key] = setIn(updated[key], path.slice(1), value);\n\t\n\t return updated;\n\t}\n\t\n\t// TODO: unit test updateIn\n\t\n\t/**\n\t * helper function to replace a nested property in an object with a new value\n\t * without mutating the object itself.\n\t *\n\t * @param {Object | Array} object\n\t * @param {Array.} path\n\t * @param {function} callback\n\t * @return {Object | Array} Returns a new, updated object or array\n\t */\n\tfunction updateIn(object, path, callback) {\n\t if (path.length === 0) {\n\t return callback(object);\n\t }\n\t\n\t var key = path[0];\n\t var updated = void 0;\n\t if (typeof key === 'string' && !(0, _typeUtils.isObject)(object)) {\n\t updated = {}; // change into an object\n\t } else if (typeof key === 'number' && !Array.isArray(object)) {\n\t updated = []; // change into an array\n\t } else {\n\t updated = clone(object);\n\t }\n\t\n\t updated[key] = updateIn(updated[key], path.slice(1), callback);\n\t\n\t return updated;\n\t}\n\t\n\t// TODO: unit test deleteIn\n\t\n\t/**\n\t * helper function to delete a nested property in an object\n\t * without mutating the object itself.\n\t *\n\t * @param {Object | Array} object\n\t * @param {Array.} path\n\t * @return {Object | Array} Returns a new, updated object or array\n\t */\n\tfunction deleteIn(object, path) {\n\t if (path.length === 0) {\n\t return object;\n\t }\n\t\n\t if (path.length === 1) {\n\t var _key = path[0];\n\t var updated = clone(object);\n\t if (Array.isArray(updated)) {\n\t updated.splice(_key, 1);\n\t } else {\n\t delete updated[_key];\n\t }\n\t\n\t return updated;\n\t }\n\t\n\t var key = path[0];\n\t var child = object[key];\n\t if (Array.isArray(child) || (0, _typeUtils.isObject)(child)) {\n\t var _updated = clone(object);\n\t _updated[key] = deleteIn(child, path.slice(1));\n\t return _updated;\n\t } else {\n\t // child property doesn't exist. just do nothing\n\t return object;\n\t }\n\t}\n\n/***/ },\n/* 6 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\t\n\tvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol ? \"symbol\" : typeof obj; };\n\t\n\texports.valueType = valueType;\n\texports.isObject = isObject;\n\texports.isUrl = isUrl;\n\texports.stringConvert = stringConvert;\n\t\n\t/**\n\t * Get the type of a value\n\t * @param {*} value\n\t * @return {String} type\n\t */\n\tfunction valueType(value) {\n\t if (value === null) {\n\t return 'null';\n\t }\n\t if (value === undefined) {\n\t return 'undefined';\n\t }\n\t if (typeof value === 'number') {\n\t return 'number';\n\t }\n\t if (typeof value === 'string') {\n\t return 'string';\n\t }\n\t if (typeof value === 'boolean') {\n\t return 'boolean';\n\t }\n\t if (value instanceof RegExp) {\n\t return 'regexp';\n\t }\n\t if (Array.isArray(value)) {\n\t return 'array';\n\t }\n\t\n\t return 'object';\n\t}\n\t\n\t/**\n\t * Test whether a value is an object (and not an Array or Date or primitive value)\n\t *\n\t * @param {*} value\n\t * @return {boolean}\n\t */\n\tfunction isObject(value) {\n\t return (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value && // not null\n\t !Array.isArray(value) && value.toString() === '[object Object]';\n\t}\n\t\n\t/**\n\t * Test whether a text contains a url (matches when a string starts\n\t * with 'http://*' or 'https://*' and has no whitespace characters)\n\t * @param {String} text\n\t */\n\tvar isUrlRegex = /^https?:\\/\\/\\S+$/;\n\tfunction isUrl(text) {\n\t return typeof text === 'string' && isUrlRegex.test(text);\n\t}\n\t\n\t/**\n\t * Convert contents of a string to the correct JSON type. This can be a string,\n\t * a number, a boolean, etc\n\t * @param {String} str\n\t * @return {*} castedStr\n\t * @private\n\t */\n\tfunction stringConvert(str) {\n\t var num = Number(str); // will nicely fail with '123ab'\n\t var numFloat = parseFloat(str); // will nicely fail with ' '\n\t\n\t if (str == '') {\n\t return '';\n\t } else if (str == 'null') {\n\t return null;\n\t } else if (str == 'true') {\n\t return true;\n\t } else if (str == 'false') {\n\t return false;\n\t } else if (!isNaN(num) && !isNaN(numFloat)) {\n\t return num;\n\t } else {\n\t return str;\n\t }\n\t}\n\n/***/ },\n/* 7 */\n/***/ function(module, exports) {\n\n\t\"use strict\";\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports.last = last;\n\texports.compareAsc = compareAsc;\n\texports.compareDesc = compareDesc;\n\t/**\n\t * Returns the last item of an array\n\t * @param {Array} array\n\t * @return {*}\n\t */\n\tfunction last(array) {\n\t return array[array.length - 1];\n\t}\n\t\n\t/**\n\t * Comparator to sort an array in ascending order\n\t *\n\t * Usage:\n\t * [4,2,5].sort(compareAsc) // [2,4,5]\n\t *\n\t * @param a\n\t * @param b\n\t * @return {number}\n\t */\n\tfunction compareAsc(a, b) {\n\t return a > b ? 1 : a < b ? -1 : 0;\n\t}\n\t\n\t/**\n\t * Comparator to sort an array in ascending order\n\t *\n\t * Usage:\n\t * [4,2,5].sort(compareDesc) // [5,4,2]\n\t *\n\t * @param a\n\t * @param b\n\t * @return {number}\n\t */\n\tfunction compareDesc(a, b) {\n\t return a > b ? -1 : a < b ? 1 : 0;\n\t}\n\n/***/ },\n/* 8 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports.default = bindMethods;\n\t/**\n\t * Helper function to bind all methods of a class instance to the instance\n\t *\n\t * Usage:\n\t *\n\t * import bindMethods from './bindMethods'\n\t *\n\t * class MyClass {\n\t * constructor () {\n\t * bindMethods(this)\n\t * }\n\t *\n\t * myMethod () {\n\t * // ...\n\t * }\n\t * }\n\t *\n\t * @param {Object} instance Instance of an ES6 class or prototype\n\t */\n\tfunction bindMethods(instance) {\n\t var prototype = Object.getPrototypeOf(instance);\n\t\n\t Object.getOwnPropertyNames(prototype).forEach(function (name) {\n\t if (typeof instance[name] === 'function') {\n\t instance[name] = instance[name].bind(instance);\n\t }\n\t });\n\t}\n\n/***/ },\n/* 9 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\t\n\tvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\t\n\tvar _preact = __webpack_require__(1);\n\t\n\tvar _ContextMenu = __webpack_require__(10);\n\t\n\tvar _ContextMenu2 = _interopRequireDefault(_ContextMenu);\n\t\n\tvar _stringUtils = __webpack_require__(11);\n\t\n\tvar _arrayUtils = __webpack_require__(7);\n\t\n\tvar _domUtils = __webpack_require__(13);\n\t\n\tvar _typeUtils = __webpack_require__(6);\n\t\n\tfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\t\n\tfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\t\n\tfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\t\n\tfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\t\n\t// TYPE_TITLES with explanation for the different types\n\tvar TYPE_TITLES = {\n\t 'value': 'Item type \"value\". ' + 'The item type is automatically determined from the value ' + 'and can be a string, number, boolean, or null.',\n\t 'object': 'Item type \"object\". ' + 'An object contains an unordered set of key/value pairs.',\n\t 'array': 'Item type \"array\". ' + 'An array contains an ordered collection of values.',\n\t 'string': 'Item type \"string\". ' + 'Item type is not determined from the value, ' + 'but always returned as string.'\n\t};\n\t\n\t/**\n\t * @type {JSONNode | null} activeContextMenu singleton holding the JSONNode having\n\t * the active (visible) context menu\n\t */\n\tvar activeContextMenu = null;\n\t\n\tvar JSONNode = function (_Component) {\n\t _inherits(JSONNode, _Component);\n\t\n\t // TODO: define propTypes\n\t\n\t function JSONNode(props) {\n\t _classCallCheck(this, JSONNode);\n\t\n\t var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(JSONNode).call(this, props));\n\t\n\t _this.state = {\n\t menu: null, // context menu\n\t appendMenu: null };\n\t\n\t // TODO: use function bindMethods(this). Gives issues for some reason, we lose focus whilst typing\n\t _this.handleChangeProperty = _this.handleChangeProperty.bind(_this);\n\t _this.handleChangeValue = _this.handleChangeValue.bind(_this);\n\t _this.handleClickValue = _this.handleClickValue.bind(_this);\n\t _this.handleKeyDownValue = _this.handleKeyDownValue.bind(_this);\n\t _this.handleExpand = _this.handleExpand.bind(_this);\n\t _this.handleContextMenu = _this.handleContextMenu.bind(_this);\n\t _this.handleAppendContextMenu = _this.handleAppendContextMenu.bind(_this);\n\t return _this;\n\t }\n\t\n\t _createClass(JSONNode, [{\n\t key: 'render',\n\t value: function render(props) {\n\t if (props.data.type === 'array') {\n\t return this.renderJSONArray(props);\n\t } else if (props.data.type === 'object') {\n\t return this.renderJSONObject(props);\n\t } else {\n\t return this.renderJSONValue(props);\n\t }\n\t }\n\t }, {\n\t key: 'renderJSONObject',\n\t value: function renderJSONObject(_ref) {\n\t var _this2 = this;\n\t\n\t var prop = _ref.prop;\n\t var data = _ref.data;\n\t var options = _ref.options;\n\t var events = _ref.events;\n\t\n\t var childCount = data.props.length;\n\t var contents = [(0, _preact.h)('div', { class: 'jsoneditor-node jsoneditor-object' }, [this.renderExpandButton(), this.renderContextMenuButton(), this.renderProperty(prop, data, options), this.renderReadonly('{' + childCount + '}', 'Array containing ' + childCount + ' items')])];\n\t\n\t if (data.expanded) {\n\t var props = data.props.map(function (prop) {\n\t return (0, _preact.h)(JSONNode, {\n\t parent: _this2,\n\t prop: prop.name,\n\t data: prop.value,\n\t options: options,\n\t events: events\n\t });\n\t });\n\t\n\t if (props.length === 0) {\n\t props.push(this.renderAppend('(empty object)'));\n\t }\n\t\n\t contents.push((0, _preact.h)('ul', { class: 'jsoneditor-list' }, props));\n\t }\n\t\n\t return (0, _preact.h)('li', {}, contents);\n\t }\n\t }, {\n\t key: 'renderJSONArray',\n\t value: function renderJSONArray(_ref2) {\n\t var _this3 = this;\n\t\n\t var prop = _ref2.prop;\n\t var data = _ref2.data;\n\t var options = _ref2.options;\n\t var events = _ref2.events;\n\t\n\t var childCount = data.items.length;\n\t var contents = [(0, _preact.h)('div', { class: 'jsoneditor-node jsoneditor-array' }, [this.renderExpandButton(), this.renderContextMenuButton(), this.renderProperty(prop, data, options), this.renderReadonly('[' + childCount + ']', 'Array containing ' + childCount + ' items')])];\n\t\n\t if (data.expanded) {\n\t var items = data.items.map(function (child, index) {\n\t return (0, _preact.h)(JSONNode, {\n\t parent: _this3,\n\t prop: index,\n\t data: child,\n\t options: options,\n\t events: events\n\t });\n\t });\n\t\n\t if (items.length === 0) {\n\t items.push(this.renderAppend('(empty array)'));\n\t }\n\t\n\t contents.push((0, _preact.h)('ul', { class: 'jsoneditor-list' }, items));\n\t }\n\t\n\t return (0, _preact.h)('li', {}, contents);\n\t }\n\t }, {\n\t key: 'renderJSONValue',\n\t value: function renderJSONValue(_ref3) {\n\t var prop = _ref3.prop;\n\t var data = _ref3.data;\n\t var options = _ref3.options;\n\t\n\t return (0, _preact.h)('li', {}, [(0, _preact.h)('div', { class: 'jsoneditor-node' }, [this.renderPlaceholder(), this.renderContextMenuButton(), this.renderProperty(prop, data, options), this.renderSeparator(), this.renderValue(data.value)])]);\n\t }\n\t\n\t /**\n\t * Render contents for an empty object or array\n\t * @param {string} text\n\t * @return {*}\n\t */\n\t\n\t }, {\n\t key: 'renderAppend',\n\t value: function renderAppend(text) {\n\t return (0, _preact.h)('li', {}, [(0, _preact.h)('div', { class: 'jsoneditor-node' }, [this.renderPlaceholder(), this.renderAppendContextMenuButton(), this.renderReadonly(text)])]);\n\t }\n\t }, {\n\t key: 'renderPlaceholder',\n\t value: function renderPlaceholder() {\n\t return (0, _preact.h)('div', { class: 'jsoneditor-button-placeholder' });\n\t }\n\t }, {\n\t key: 'renderReadonly',\n\t value: function renderReadonly(text) {\n\t var title = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];\n\t\n\t return (0, _preact.h)('div', { class: 'jsoneditor-readonly', title: title }, text);\n\t }\n\t }, {\n\t key: 'renderProperty',\n\t value: function renderProperty(prop, data, options) {\n\t if (prop !== null) {\n\t var isIndex = typeof prop === 'number';\n\t\n\t if (isIndex) {\n\t // array item\n\t return (0, _preact.h)('div', {\n\t class: 'jsoneditor-property jsoneditor-readonly',\n\t spellCheck: 'false'\n\t }, prop);\n\t } else {\n\t // object property\n\t return (0, _preact.h)('div', {\n\t class: 'jsoneditor-property',\n\t contentEditable: 'true',\n\t spellCheck: 'false',\n\t onInput: this.handleChangeProperty\n\t }, prop);\n\t }\n\t } else {\n\t // root node\n\t var content = JSONNode._rootName(data, options);\n\t\n\t return (0, _preact.h)('div', {\n\t class: 'jsoneditor-property jsoneditor-readonly',\n\t spellCheck: 'false',\n\t onInput: this.handleChangeProperty\n\t }, content);\n\t }\n\t }\n\t }, {\n\t key: 'renderSeparator',\n\t value: function renderSeparator() {\n\t return (0, _preact.h)('div', { class: 'jsoneditor-separator' }, ':');\n\t }\n\t }, {\n\t key: 'renderValue',\n\t value: function renderValue(value) {\n\t var type = (0, _typeUtils.valueType)(value);\n\t var _isUrl = (0, _typeUtils.isUrl)(value);\n\t var valueClass = 'jsoneditor-value jsoneditor-' + type + (_isUrl ? ' jsoneditor-url' : '');\n\t\n\t return (0, _preact.h)('div', {\n\t class: valueClass,\n\t contentEditable: 'true',\n\t spellCheck: 'false',\n\t onInput: this.handleChangeValue,\n\t onClick: this.handleClickValue,\n\t onKeyDown: this.handleKeyDownValue,\n\t title: _isUrl ? 'Ctrl+Click or ctrl+Enter to open url' : null\n\t }, (0, _stringUtils.escapeHTML)(value));\n\t }\n\t }, {\n\t key: 'renderExpandButton',\n\t value: function renderExpandButton() {\n\t var className = 'jsoneditor-button jsoneditor-' + (this.props.data.expanded ? 'expanded' : 'collapsed');\n\t return (0, _preact.h)('div', { class: 'jsoneditor-button-container' }, (0, _preact.h)('button', { class: className, onClick: this.handleExpand }));\n\t }\n\t }, {\n\t key: 'renderContextMenuButton',\n\t value: function renderContextMenuButton() {\n\t var className = 'jsoneditor-button jsoneditor-contextmenu' + (this.state.menu ? ' jsoneditor-visible' : '');\n\t\n\t return (0, _preact.h)('div', { class: 'jsoneditor-button-container' }, [this.renderContextMenu(this.state.menu), (0, _preact.h)('button', { class: className, onClick: this.handleContextMenu })]);\n\t }\n\t }, {\n\t key: 'renderAppendContextMenuButton',\n\t value: function renderAppendContextMenuButton() {\n\t var className = 'jsoneditor-button jsoneditor-contextmenu' + (this.state.appendMenu ? ' jsoneditor-visible' : '');\n\t\n\t return (0, _preact.h)('div', { class: 'jsoneditor-button-container' }, [this.renderAppendContextMenu(), (0, _preact.h)('button', { class: className, onClick: this.handleAppendContextMenu })]);\n\t }\n\t }, {\n\t key: 'renderContextMenu',\n\t value: function renderContextMenu() {\n\t var _this4 = this;\n\t\n\t if (!this.state.menu) {\n\t return null;\n\t }\n\t\n\t var _state$menu = this.state.menu;\n\t var anchor = _state$menu.anchor;\n\t var root = _state$menu.root;\n\t\n\t var path = this.getPath();\n\t var hasParent = this.props.parent !== null;\n\t var type = this.props.data.type;\n\t var events = this.props.events;\n\t var items = []; // array with menu items\n\t\n\t items.push({\n\t text: 'Type',\n\t title: 'Change the type of this field',\n\t className: 'jsoneditor-type-' + type,\n\t submenu: [{\n\t text: 'Value',\n\t className: 'jsoneditor-type-value' + (type == 'value' ? ' jsoneditor-selected' : ''),\n\t title: TYPE_TITLES.value,\n\t click: function click() {\n\t return events.onChangeType(path, 'value');\n\t }\n\t }, {\n\t text: 'Array',\n\t className: 'jsoneditor-type-array' + (type == 'array' ? ' jsoneditor-selected' : ''),\n\t title: TYPE_TITLES.array,\n\t click: function click() {\n\t return events.onChangeType(path, 'array');\n\t }\n\t }, {\n\t text: 'Object',\n\t className: 'jsoneditor-type-object' + (type == 'object' ? ' jsoneditor-selected' : ''),\n\t title: TYPE_TITLES.object,\n\t click: function click() {\n\t return events.onChangeType(path, 'object');\n\t }\n\t }, {\n\t text: 'String',\n\t className: 'jsoneditor-type-string' + (type == 'string' ? ' jsoneditor-selected' : ''),\n\t title: TYPE_TITLES.string,\n\t click: function click() {\n\t return events.onChangeType(path, 'string');\n\t }\n\t }]\n\t });\n\t\n\t if (type === 'array' || type === 'object') {\n\t var direction = this.sortOrder == 'asc' ? 'desc' : 'asc';\n\t items.push({\n\t text: 'Sort',\n\t title: 'Sort the childs of this ' + TYPE_TITLES.type,\n\t className: 'jsoneditor-sort-' + direction,\n\t click: function click() {\n\t return events.onSort(path);\n\t },\n\t submenu: [{\n\t text: 'Ascending',\n\t className: 'jsoneditor-sort-asc',\n\t title: 'Sort the childs of this ' + TYPE_TITLES.type + ' in ascending order',\n\t click: function click() {\n\t return events.onSort(path, 'asc');\n\t }\n\t }, {\n\t text: 'Descending',\n\t className: 'jsoneditor-sort-desc',\n\t title: 'Sort the childs of this ' + TYPE_TITLES.type + ' in descending order',\n\t click: function click() {\n\t return events.onSort(path, 'desc');\n\t }\n\t }]\n\t });\n\t }\n\t\n\t if (hasParent) {\n\t (function () {\n\t var parentPath = _this4.props.parent.getPath();\n\t var prop = _this4.props.prop;\n\t\n\t if (items.length) {\n\t // create a separator\n\t items.push({\n\t 'type': 'separator'\n\t });\n\t }\n\t\n\t // create insert button\n\t items.push({\n\t text: 'Insert',\n\t title: 'Insert a new item with type \\'value\\' after this item (Ctrl+Ins)',\n\t submenuTitle: 'Select the type of the item to be inserted',\n\t className: 'jsoneditor-insert',\n\t click: function click() {\n\t return events.onInsert(parentPath, prop, 'value');\n\t },\n\t submenu: [{\n\t text: 'Value',\n\t className: 'jsoneditor-type-value',\n\t title: TYPE_TITLES.value,\n\t click: function click() {\n\t return events.onInsert(parentPath, prop, 'value');\n\t }\n\t }, {\n\t text: 'Array',\n\t className: 'jsoneditor-type-array',\n\t title: TYPE_TITLES.array,\n\t click: function click() {\n\t return events.onInsert(parentPath, prop, 'array');\n\t }\n\t }, {\n\t text: 'Object',\n\t className: 'jsoneditor-type-object',\n\t title: TYPE_TITLES.object,\n\t click: function click() {\n\t return events.onInsert(parentPath, prop, 'object');\n\t }\n\t }, {\n\t text: 'String',\n\t className: 'jsoneditor-type-string',\n\t title: TYPE_TITLES.string,\n\t click: function click() {\n\t return events.onInsert(parentPath, prop, 'string');\n\t }\n\t }]\n\t });\n\t\n\t // create duplicate button\n\t items.push({\n\t text: 'Duplicate',\n\t title: 'Duplicate this item (Ctrl+D)',\n\t className: 'jsoneditor-duplicate',\n\t click: function click() {\n\t return events.onDuplicate(parentPath, prop);\n\t }\n\t });\n\t\n\t // create remove button\n\t items.push({\n\t text: 'Remove',\n\t title: 'Remove this item (Ctrl+Del)',\n\t className: 'jsoneditor-remove',\n\t click: function click() {\n\t return events.onRemove(parentPath, prop);\n\t }\n\t });\n\t })();\n\t }\n\t\n\t // TODO: implement a hook to adjust the context menu\n\t\n\t return (0, _preact.h)(_ContextMenu2.default, { anchor: anchor, root: root, items: items });\n\t }\n\t }, {\n\t key: 'renderAppendContextMenu',\n\t value: function renderAppendContextMenu() {\n\t if (!this.state.appendMenu) {\n\t return null;\n\t }\n\t\n\t var _state$appendMenu = this.state.appendMenu;\n\t var anchor = _state$appendMenu.anchor;\n\t var root = _state$appendMenu.root;\n\t\n\t var path = this.getPath();\n\t var events = this.props.events;\n\t var items = []; // array with menu items\n\t\n\t // create insert button\n\t items.push({\n\t text: 'Insert',\n\t title: 'Insert a new item with type \\'value\\' after this item (Ctrl+Ins)',\n\t submenuTitle: 'Select the type of the item to be inserted',\n\t className: 'jsoneditor-insert',\n\t click: function click() {\n\t return events.onAppend(path, 'value');\n\t },\n\t submenu: [{\n\t text: 'Value',\n\t className: 'jsoneditor-type-value',\n\t title: TYPE_TITLES.value,\n\t click: function click() {\n\t return events.onAppend(path, 'value');\n\t }\n\t }, {\n\t text: 'Array',\n\t className: 'jsoneditor-type-array',\n\t title: TYPE_TITLES.array,\n\t click: function click() {\n\t return events.onAppend(path, 'array');\n\t }\n\t }, {\n\t text: 'Object',\n\t className: 'jsoneditor-type-object',\n\t title: TYPE_TITLES.object,\n\t click: function click() {\n\t return events.onAppend(path, 'object');\n\t }\n\t }, {\n\t text: 'String',\n\t className: 'jsoneditor-type-string',\n\t title: TYPE_TITLES.string,\n\t click: function click() {\n\t return events.onAppend(path, 'string');\n\t }\n\t }]\n\t });\n\t\n\t // TODO: implement a hook to adjust the context menu\n\t\n\t return (0, _preact.h)(_ContextMenu2.default, { anchor: anchor, root: root, items: items });\n\t }\n\t }, {\n\t key: 'shouldComponentUpdate',\n\t value: function shouldComponentUpdate(nextProps, nextState) {\n\t var prop = void 0;\n\t\n\t for (prop in nextProps) {\n\t if (nextProps.hasOwnProperty(prop) && this.props[prop] !== nextProps[prop]) {\n\t return true;\n\t }\n\t }\n\t\n\t for (prop in nextState) {\n\t if (nextState.hasOwnProperty(prop) && this.state[prop] !== nextState[prop]) {\n\t return true;\n\t }\n\t }\n\t\n\t return false;\n\t }\n\t }, {\n\t key: 'handleChangeProperty',\n\t value: function handleChangeProperty(event) {\n\t var parentPath = this.props.parent.getPath();\n\t var oldProp = this.props.prop;\n\t var newProp = (0, _stringUtils.unescapeHTML)((0, _domUtils.getInnerText)(event.target));\n\t\n\t this.props.events.onChangeProperty(parentPath, oldProp, newProp);\n\t }\n\t }, {\n\t key: 'handleChangeValue',\n\t value: function handleChangeValue(event) {\n\t var value = this._getValueFromEvent(event);\n\t\n\t this.props.events.onChangeValue(this.getPath(), value);\n\t }\n\t }, {\n\t key: 'handleClickValue',\n\t value: function handleClickValue(event) {\n\t if (event.ctrlKey && event.button === 0) {\n\t // Ctrl+Left click\n\t this._openLinkIfUrl(event);\n\t }\n\t }\n\t }, {\n\t key: 'handleKeyDownValue',\n\t value: function handleKeyDownValue(event) {\n\t if (event.ctrlKey && event.which === 13) {\n\t // Ctrl+Enter\n\t this._openLinkIfUrl(event);\n\t }\n\t }\n\t }, {\n\t key: 'handleExpand',\n\t value: function handleExpand(event) {\n\t this.props.events.onExpand(this.getPath(), !this.props.data.expanded);\n\t }\n\t }, {\n\t key: 'handleContextMenu',\n\t value: function handleContextMenu(event) {\n\t event.stopPropagation();\n\t\n\t if (this.state.menu) {\n\t // hide context menu\n\t JSONNode.hideContextMenu();\n\t } else {\n\t // hide any currently visible context menu\n\t JSONNode.hideContextMenu();\n\t\n\t // show context menu\n\t this.setState({\n\t menu: {\n\t anchor: event.target,\n\t root: JSONNode._findRootElement(event)\n\t }\n\t });\n\t activeContextMenu = this;\n\t }\n\t }\n\t }, {\n\t key: 'handleAppendContextMenu',\n\t value: function handleAppendContextMenu(event) {\n\t event.stopPropagation();\n\t\n\t if (this.state.appendMenu) {\n\t // hide append context menu\n\t JSONNode.hideContextMenu();\n\t } else {\n\t // hide any currently visible context menu\n\t JSONNode.hideContextMenu();\n\t\n\t // show append context menu\n\t this.setState({\n\t appendMenu: {\n\t anchor: event.target,\n\t root: JSONNode._findRootElement(event)\n\t }\n\t });\n\t activeContextMenu = this;\n\t }\n\t }\n\t\n\t /**\n\t * Singleton function to hide the currently visible context menu if any.\n\t */\n\t\n\t }, {\n\t key: '_openLinkIfUrl',\n\t\n\t\n\t /**\n\t * When this JSONNode holds an URL as value, open this URL in a new browser tab\n\t * @param event\n\t * @private\n\t */\n\t value: function _openLinkIfUrl(event) {\n\t var value = this._getValueFromEvent(event);\n\t\n\t if ((0, _typeUtils.isUrl)(value)) {\n\t event.preventDefault();\n\t event.stopPropagation();\n\t\n\t window.open(value, '_blank');\n\t }\n\t }\n\t\n\t /**\n\t * Get the path of this JSONNode\n\t * @return {Array.}\n\t */\n\t\n\t }, {\n\t key: 'getPath',\n\t value: function getPath() {\n\t var path = this.props.parent ? this.props.parent.getPath() : [];\n\t\n\t if (this.props.prop !== null) {\n\t path.push(this.props.prop);\n\t }\n\t\n\t return path;\n\t }\n\t\n\t /**\n\t * Get the value of the target of an event, and convert it to it's type\n\t * @param event\n\t * @return {string | number | boolean | null}\n\t * @private\n\t */\n\t\n\t }, {\n\t key: '_getValueFromEvent',\n\t value: function _getValueFromEvent(event) {\n\t var stringValue = (0, _stringUtils.unescapeHTML)((0, _domUtils.getInnerText)(event.target));\n\t return this.props.data.type === 'string' ? stringValue : (0, _typeUtils.stringConvert)(stringValue);\n\t }\n\t\n\t /**\n\t * Find the root DOM element of the JSONEditor\n\t * Search is done based on the CSS class 'jsoneditor'\n\t * @param event\n\t * @return {*}\n\t * @private\n\t */\n\t\n\t }], [{\n\t key: '_rootName',\n\t value: function _rootName(data, options) {\n\t return typeof options.name === 'string' ? options.name : data.type === 'object' || data.type === 'array' ? data.type : (0, _typeUtils.valueType)(data.value);\n\t }\n\t }, {\n\t key: 'hideContextMenu',\n\t value: function hideContextMenu() {\n\t if (activeContextMenu) {\n\t activeContextMenu.setState({\n\t menu: null,\n\t appendMenu: null\n\t });\n\t activeContextMenu = null;\n\t }\n\t }\n\t }, {\n\t key: '_findRootElement',\n\t value: function _findRootElement(event) {\n\t function isEditorElement(elem) {\n\t return elem.className.split(' ').indexOf('jsoneditor') !== -1;\n\t }\n\t\n\t var elem = event.target;\n\t while (elem) {\n\t if (isEditorElement(elem)) {\n\t return elem;\n\t }\n\t\n\t elem = elem.parentNode;\n\t }\n\t\n\t return null;\n\t }\n\t }]);\n\t\n\t return JSONNode;\n\t}(_preact.Component);\n\t\n\texports.default = JSONNode;\n\n/***/ },\n/* 10 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports.CONTEXT_MENU_HEIGHT = undefined;\n\t\n\tvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\t\n\tvar _preact = __webpack_require__(1);\n\t\n\tfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\t\n\tfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\t\n\tfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\t\n\tvar CONTEXT_MENU_HEIGHT = exports.CONTEXT_MENU_HEIGHT = 240;\n\t\n\tvar ContextMenu = function (_Component) {\n\t _inherits(ContextMenu, _Component);\n\t\n\t function ContextMenu(props) {\n\t _classCallCheck(this, ContextMenu);\n\t\n\t // determine orientation\n\t var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ContextMenu).call(this, props));\n\t\n\t var anchorRect = _this.props.anchor.getBoundingClientRect();\n\t var rootRect = _this.props.root.getBoundingClientRect();\n\t var orientation = rootRect.bottom - anchorRect.bottom < CONTEXT_MENU_HEIGHT && anchorRect.top - rootRect.top > CONTEXT_MENU_HEIGHT ? 'top' : 'bottom';\n\t\n\t _this.state = {\n\t orientation: orientation,\n\t expanded: null, // menu index of expanded menu item\n\t expanding: null, // menu index of expanding menu item\n\t collapsing: null // menu index of collapsing menu item\n\t };\n\t\n\t _this.renderMenuItem = _this.renderMenuItem.bind(_this);\n\t return _this;\n\t }\n\t\n\t _createClass(ContextMenu, [{\n\t key: 'render',\n\t value: function render() {\n\t if (!this.props.items) {\n\t return null;\n\t }\n\t\n\t // TODO: create a non-visible button to set the focus to the menu\n\t // TODO: implement (customizable) quick keys\n\t\n\t var className = 'jsoneditor-contextmenu ' + (this.state.orientation === 'top' ? 'jsoneditor-contextmenu-top' : 'jsoneditor-contextmenu-bottom');\n\t\n\t return (0, _preact.h)('div', { class: className }, this.props.items.map(this.renderMenuItem));\n\t }\n\t }, {\n\t key: 'renderMenuItem',\n\t value: function renderMenuItem(item, index) {\n\t if (item.type === 'separator') {\n\t return (0, _preact.h)('div', { class: 'jsoneditor-menu-separator' });\n\t }\n\t\n\t if (item.click && item.submenu) {\n\t // two buttons: direct click and a small button to expand the submenu\n\t return (0, _preact.h)('div', { class: 'jsoneditor-menu-item' }, [(0, _preact.h)('button', { class: 'jsoneditor-menu-button jsoneditor-menu-default ' + item.className, title: item.title, onClick: item.click }, [(0, _preact.h)('span', { class: 'jsoneditor-icon' }), (0, _preact.h)('span', { class: 'jsoneditor-text' }, item.text)]), (0, _preact.h)('button', { class: 'jsoneditor-menu-button jsoneditor-menu-expand', onClick: this.createExpandHandler(index) }, [(0, _preact.h)('span', { class: 'jsoneditor-icon jsoneditor-icon-expand' })]), this.renderSubMenu(item.submenu, index)]);\n\t } else if (item.submenu) {\n\t // button expands the submenu\n\t return (0, _preact.h)('div', { class: 'jsoneditor-menu-item' }, [(0, _preact.h)('button', { class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: this.createExpandHandler(index) }, [(0, _preact.h)('span', { class: 'jsoneditor-icon' }), (0, _preact.h)('span', { class: 'jsoneditor-text' }, item.text), (0, _preact.h)('span', { class: 'jsoneditor-icon jsoneditor-icon-expand' })]), this.renderSubMenu(item.submenu, index)]);\n\t } else {\n\t // just a button (no submenu)\n\t return (0, _preact.h)('div', { class: 'jsoneditor-menu-item' }, [(0, _preact.h)('button', { class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: item.click }, [(0, _preact.h)('span', { class: 'jsoneditor-icon' }), (0, _preact.h)('span', { class: 'jsoneditor-text' }, item.text)])]);\n\t }\n\t }\n\t\n\t /**\n\t * @param {Array} submenu\n\t * @param {number} index\n\t */\n\t\n\t }, {\n\t key: 'renderSubMenu',\n\t value: function renderSubMenu(submenu, index) {\n\t var expanded = this.state.expanded === index;\n\t var collapsing = this.state.collapsing === index;\n\t\n\t var contents = submenu.map(function (item) {\n\t return (0, _preact.h)('div', { class: 'jsoneditor-menu-item' }, [(0, _preact.h)('button', { class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: item.click }, [(0, _preact.h)('span', { class: 'jsoneditor-icon' }), (0, _preact.h)('span', { class: 'jsoneditor-text' }, item.text)])]);\n\t });\n\t\n\t var className = 'jsoneditor-submenu ' + (expanded ? ' jsoneditor-expanded' : '') + (collapsing ? ' jsoneditor-collapsing' : '');\n\t\n\t return (0, _preact.h)('div', { class: className }, contents);\n\t }\n\t }, {\n\t key: 'createExpandHandler',\n\t value: function createExpandHandler(index) {\n\t var _this2 = this;\n\t\n\t return function (event) {\n\t event.stopPropagation();\n\t\n\t var prev = _this2.state.expanded;\n\t\n\t _this2.setState({\n\t expanded: prev === index ? null : index,\n\t collapsing: prev\n\t });\n\t\n\t // timeout after unit is collapsed\n\t setTimeout(function () {\n\t if (prev === _this2.state.collapsing) {\n\t _this2.setState({\n\t collapsing: null\n\t });\n\t }\n\t }, 300);\n\t };\n\t }\n\t }]);\n\t\n\t return ContextMenu;\n\t}(_preact.Component);\n\t\n\texports.default = ContextMenu;\n\n/***/ },\n/* 11 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports.escapeHTML = escapeHTML;\n\texports.unescapeHTML = unescapeHTML;\n\texports.escapeJSON = escapeJSON;\n\t\n\tvar _jsonUtils = __webpack_require__(12);\n\t\n\t/**\n\t * escape a text, such that it can be displayed safely in an HTML element\n\t * @param {String} text\n\t * @param {boolean} [escapeUnicode=false]\n\t * @return {String} escapedText\n\t */\n\tfunction escapeHTML(text) {\n\t var escapeUnicode = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];\n\t\n\t if (typeof text !== 'string') {\n\t return String(text);\n\t } else {\n\t var htmlEscaped = String(text).replace(/ /g, '  ') // replace double space with an nbsp and space\n\t .replace(/^ /, ' ') // space at start\n\t .replace(/ $/, ' '); // space at end\n\t\n\t var json = JSON.stringify(htmlEscaped);\n\t var html = json.substring(1, json.length - 1);\n\t if (escapeUnicode === true) {\n\t html = escapeUnicodeChars(html);\n\t }\n\t return html;\n\t }\n\t}\n\t\n\t/**\n\t * Escape unicode characters.\n\t * For example input '\\u2661' (length 1) will output '\\\\u2661' (length 5).\n\t * @param {string} text\n\t * @return {string}\n\t */\n\tfunction escapeUnicodeChars(text) {\n\t // see https://www.wikiwand.com/en/UTF-16\n\t // note: we leave surrogate pairs as two individual chars,\n\t // as JSON doesn't interpret them as a single unicode char.\n\t return text.replace(/[\\u007F-\\uFFFF]/g, function (c) {\n\t return '\\\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);\n\t });\n\t}\n\t\n\t/**\n\t * unescape a string.\n\t * @param {String} escapedText\n\t * @return {String} text\n\t */\n\tfunction unescapeHTML(escapedText) {\n\t var json = '\"' + escapeJSON(escapedText) + '\"';\n\t var htmlEscaped = (0, _jsonUtils.parseJSON)(json);\n\t\n\t return htmlEscaped.replace(/\\u00A0/g, ' '); // nbsp character\n\t}\n\t\n\t/**\n\t * escape a text to make it a valid JSON string. The method will:\n\t * - replace unescaped double quotes with '\\\"'\n\t * - replace unescaped backslash with '\\\\'\n\t * - replace returns with '\\n'\n\t * @param {String} text\n\t * @return {String} escapedText\n\t * @private\n\t */\n\tfunction escapeJSON(text) {\n\t // TODO: replace with some smart regex (only when a new solution is faster!)\n\t var escaped = '';\n\t var i = 0;\n\t while (i < text.length) {\n\t var c = text.charAt(i);\n\t if (c == '\\n') {\n\t escaped += '\\\\n';\n\t } else if (c == '\\\\') {\n\t escaped += c;\n\t i++;\n\t\n\t c = text.charAt(i);\n\t if (c === '' || '\"\\\\/bfnrtu'.indexOf(c) == -1) {\n\t escaped += '\\\\'; // no valid escape character\n\t }\n\t escaped += c;\n\t } else if (c == '\"') {\n\t escaped += '\\\\\"';\n\t } else {\n\t escaped += c;\n\t }\n\t i++;\n\t }\n\t\n\t return escaped;\n\t}\n\n/***/ },\n/* 12 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports.parseJSON = parseJSON;\n\texports.validate = validate;\n\t/**\n\t * Parse JSON using the parser built-in in the browser.\n\t * On exception, the jsonString is validated and a detailed error is thrown.\n\t * @param {String} jsonString\n\t * @return {JSON} json\n\t */\n\tfunction parseJSON(jsonString) {\n\t try {\n\t return JSON.parse(jsonString);\n\t } catch (err) {\n\t // try to throw a more detailed error message using validate\n\t validate(jsonString);\n\t\n\t // rethrow the original error\n\t throw err;\n\t }\n\t}\n\t\n\t/**\n\t * Validate a string containing a JSON object\n\t * This method uses JSONLint to validate the String. If JSONLint is not\n\t * available, the built-in JSON parser of the browser is used.\n\t * @param {String} jsonString String with an (invalid) JSON object\n\t * @throws Error\n\t */\n\tfunction validate(jsonString) {\n\t if (typeof window.jsonlint !== 'undefined') {\n\t window.jsonlint.parse(jsonString);\n\t } else {\n\t JSON.parse(jsonString);\n\t }\n\t}\n\n/***/ },\n/* 13 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tObject.defineProperty(exports, \"__esModule\", {\n\t value: true\n\t});\n\texports.getInnerText = getInnerText;\n\texports.getInternetExplorerVersion = getInternetExplorerVersion;\n\t/**\n\t * Get the inner text of an HTML element (for example a div element)\n\t * @param {Element} element\n\t * @param {Object} [buffer]\n\t * @return {String} innerText\n\t */\n\tfunction getInnerText(element, buffer) {\n\t var first = buffer == undefined;\n\t if (first) {\n\t buffer = {\n\t 'text': '',\n\t 'flush': function flush() {\n\t var text = this.text;\n\t this.text = '';\n\t return text;\n\t },\n\t 'set': function set(text) {\n\t this.text = text;\n\t }\n\t };\n\t }\n\t\n\t // text node\n\t if (element.nodeValue) {\n\t return buffer.flush() + element.nodeValue;\n\t }\n\t\n\t // divs or other HTML elements\n\t if (element.hasChildNodes()) {\n\t var childNodes = element.childNodes;\n\t var innerText = '';\n\t\n\t for (var i = 0, iMax = childNodes.length; i < iMax; i++) {\n\t var child = childNodes[i];\n\t\n\t if (child.nodeName == 'DIV' || child.nodeName == 'P') {\n\t var prevChild = childNodes[i - 1];\n\t var prevName = prevChild ? prevChild.nodeName : undefined;\n\t if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') {\n\t innerText += '\\n';\n\t buffer.flush();\n\t }\n\t innerText += getInnerText(child, buffer);\n\t buffer.set('\\n');\n\t } else if (child.nodeName == 'BR') {\n\t innerText += buffer.flush();\n\t buffer.set('\\n');\n\t } else {\n\t innerText += getInnerText(child, buffer);\n\t }\n\t }\n\t\n\t return innerText;\n\t } else {\n\t if (element.nodeName == 'P' && getInternetExplorerVersion() != -1) {\n\t // On Internet Explorer, a

with hasChildNodes()==false is\n\t // rendered with a new line. Note that a

with\n\t // hasChildNodes()==true is rendered without a new line\n\t // Other browsers always ensure there is a
inside the

,\n\t // and if not, the

does not render a new line\n\t return buffer.flush();\n\t }\n\t }\n\t\n\t // br or unknown\n\t return '';\n\t}\n\t\n\t/**\n\t * Returns the version of Internet Explorer or a -1\n\t * (indicating the use of another browser).\n\t * Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx\n\t * @return {Number} Internet Explorer version, or -1 in case of an other browser\n\t */\n\tfunction getInternetExplorerVersion() {\n\t if (_ieVersion == -1) {\n\t var rv = -1; // Return value assumes failure.\n\t if (navigator.appName == 'Microsoft Internet Explorer') {\n\t var ua = navigator.userAgent;\n\t var re = new RegExp(\"MSIE ([0-9]{1,}[\\.0-9]{0,})\");\n\t if (re.exec(ua) != null) {\n\t rv = parseFloat(RegExp.$1);\n\t }\n\t }\n\t\n\t _ieVersion = rv;\n\t }\n\t\n\t return _ieVersion;\n\t}\n\t\n\t/**\n\t * cached internet explorer version\n\t * @type {Number}\n\t * @private\n\t */\n\tvar _ieVersion = -1;\n\n/***/ },\n/* 14 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t// style-loader: Adds some css to the DOM by adding a - diff --git a/gulpfile.js b/gulpfile.js index e78d4e2..5be0f7b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,19 +1,14 @@ var fs = require('fs'); var gulp = require('gulp'); var gutil = require('gulp-util'); -var concatCss = require('gulp-concat-css'); -var minifyCSS = require('gulp-clean-css'); var shell = require('gulp-shell'); var mkdirp = require('mkdirp'); var webpack = require('webpack'); -var uglify = require('uglify-js'); var NAME = 'jsoneditor'; var NAME_MINIMALIST = 'jsoneditor-minimalist'; -var ENTRY = './src/js/JSONEditor.js'; -var HEADER = './src/js/header.js'; -var IMAGE = './src/css/img/jsoneditor-icons.svg'; -var DOCS = './src/docs/*'; +var ENTRY = './src/index.js'; +var HEADER = './src/header.js'; var DIST = './dist'; // generate banner with today's date and correct version @@ -31,20 +26,29 @@ var bannerPlugin = new webpack.BannerPlugin(createBanner(), { raw: true }); +var loaders = [ + { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }, + { test: /\.json$/, loader: 'json' }, + { test: /\.less$/, loaders: '!style!css!less!' }, + { test: /\.svg$/, loader: 'svg-url-loader' } +]; + // create a single instance of the compiler to allow caching var compiler = webpack({ entry: ENTRY, + devtool: 'source-map', output: { - library: 'JSONEditor', + library: 'jsoneditor', libraryTarget: 'umd', path: DIST, filename: NAME + '.js' }, - plugins: [ bannerPlugin ], + plugins: [ + bannerPlugin, + new webpack.optimize.UglifyJsPlugin() + ], module: { - loaders: [ - { test: /\.json$/, loader: "json" } - ] + loaders: loaders }, cache: true }); @@ -52,8 +56,9 @@ var compiler = webpack({ // create a single instance of the compiler to allow caching var compilerMinimalist = webpack({ entry: ENTRY, + devtool: 'source-map', output: { - library: 'JSONEditor', + library: 'jsoneditor', libraryTarget: 'umd', path: DIST, filename: NAME_MINIMALIST + '.js' @@ -61,33 +66,18 @@ var compilerMinimalist = webpack({ plugins: [ bannerPlugin, new webpack.IgnorePlugin(new RegExp('^brace$')), - new webpack.IgnorePlugin(new RegExp('^ajv')) + new webpack.IgnorePlugin(new RegExp('^ajv')), + new webpack.optimize.UglifyJsPlugin() ], + module: { + loaders: loaders + }, cache: true }); -function minify(name) { - var result = uglify.minify([DIST + '/' + name + '.js'], { - outSourceMap: name + '.map', - output: { - comments: /@license/ - } - }); - - var fileMin = DIST + '/' + name + '.min.js'; - var fileMap = DIST + '/' + name + '.map'; - - fs.writeFileSync(fileMin, result.code); - fs.writeFileSync(fileMap, result.map); - - gutil.log('Minified ' + fileMin); - gutil.log('Mapped ' + fileMap); -} - -// make dist and dist/img folders +// make dist folder gulp.task('mkdir', function () { mkdirp.sync(DIST); - mkdirp.sync(DIST + '/img'); }); // bundle javascript @@ -122,47 +112,6 @@ gulp.task('bundle-minimalist', ['mkdir'], function (done) { }); }); -// bundle css -gulp.task('bundle-css', ['mkdir'], function () { - gulp.src([ - 'src/css/reset.css', - 'src/css/jsoneditor.css', - 'src/css/contextmenu.css', - 'src/css/menu.css', - 'src/css/searchbox.css' - ]) - .pipe(concatCss(NAME + '.css')) - .pipe(gulp.dest(DIST)) - .pipe(concatCss(NAME + '.min.css')) - .pipe(minifyCSS()) - .pipe(gulp.dest(DIST)); - - gutil.log('bundled ' + DIST + '/' + NAME + '.css'); - gutil.log('bundled ' + DIST + '/' + NAME + '.min.css'); -}); - -// create a folder img and copy the icons -gulp.task('copy-img', ['mkdir'], function () { - gulp.src(IMAGE) - .pipe(gulp.dest(DIST +'/img')); - gutil.log('Copied images'); -}); - -// create a folder img and copy the icons -gulp.task('copy-docs', ['mkdir'], function () { - gulp.src(DOCS) - .pipe(gulp.dest(DIST)); - gutil.log('Copied doc'); -}); - -gulp.task('minify', ['bundle'], function () { - minify(NAME) -}); - -gulp.task('minify-minimalist', ['bundle-minimalist'], function () { - minify(NAME_MINIMALIST) -}); - // TODO: zip file using archiver var pkg = 'jsoneditor-' + require('./package.json').version + '.zip'; gulp.task('zip', shell.task([ @@ -172,17 +121,14 @@ gulp.task('zip', shell.task([ // The watch task (to automatically rebuild when the source code changes) // Does only generate jsoneditor.js and jsoneditor.css, and copy the image // Does NOT minify the code and does NOT generate the minimalist version -gulp.task('watch', ['bundle', 'bundle-css', 'copy-img'], function () { - gulp.watch(['src/**/*'], ['bundle', 'bundle-css', 'copy-img']); +gulp.task('watch', ['bundle'], function () { + // TODO: don't minify when in watch mode + + gulp.watch(['src/**/*'], ['bundle']); }); // The default task (called when you run `gulp`) gulp.task('default', [ 'bundle', - 'bundle-minimalist', - 'bundle-css', - 'copy-img', - 'copy-docs', - 'minify', - 'minify-minimalist' + 'bundle-minimalist' ]); diff --git a/package.json b/package.json index 88c7622..feac4ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jsoneditor", - "version": "5.5.6", + "version": "6.0.0-BETA", "main": "./index", "description": "A web-based tool to view, edit, format, and validate JSON", "tags": [ @@ -10,7 +10,7 @@ "formatter" ], "author": "Jos de Jong ", - "license": "Apache-2.0", + "license": "MIT", "homepage": "https://github.com/josdejong/jsoneditor", "repository": { "type": "git", @@ -18,38 +18,36 @@ }, "bugs": "https://github.com/josdejong/jsoneditor/issues", "scripts": { - "build": "react-dev-server --static dist --build; mv dist/bundle.js dist/jsoneditor.js; uglifyjs dist/jsoneditor.js -o dist/jsoneditor.min.js; cp src/jsoneditor.css dist", - "watch": "react-dev-server --static src --index develop.html", - "test": "mocha test" + "build": "gulp", + "test": "ava test/*.test.js test/**/*.test.js", + "watch": "gulp watch" }, "dependencies": { - "ajv": "3.8.8", + "ajv": "4.4.0", "brace": "0.8.0", - "javascript-natural-sort": "0.7.1" + "javascript-natural-sort": "0.7.1", + "preact": "5.6.0" }, "devDependencies": { - "babel-plugin-transform-react-jsx": "6.8.0", - "browserify": "13.0.1", - "buble": "0.12.5", - "debug": "2.2.0", + "ava": "0.16.0", + "babel-core": "6.13.2", + "babel-loader": "6.2.4", + "css-loader": "0.23.1", "gulp": "3.9.1", - "gulp-clean-css": "2.0.5", - "gulp-concat-css": "2.2.0", "gulp-shell": "0.5.2", "gulp-util": "3.0.7", "json-loader": "0.5.4", - "json-pointer": "0.5.0", - "mithril": "0.2.5", + "less": "2.7.1", + "less-loader": "2.2.3", "mkdirp": "0.5.1", - "mocha": "2.4.5", - "preact": "5.6.0", - "reify": "0.3.6", - "rollup": "0.34.1", - "rollup-plugin-buble": "0.12.1", - "rollup-plugin-commonjs": "3.1.0", - "rollup-plugin-node-resolve": "1.7.1", - "rollup-plugin-npm": "2.0.0", - "uglify-js": "2.6.2", - "webpack": "1.12.14" + "style-loader": "0.13.1", + "svg-url-loader": "1.1.0", + "webpack": "1.13.1" + }, + "ava": { + "require": [ + "babel-register" + ], + "babel": "inherit" } } diff --git a/src/develop.html b/src/develop.html index 81b7ef9..f730186 100644 --- a/src/develop.html +++ b/src/develop.html @@ -3,8 +3,7 @@ Develop JSONEditor Next - - +

diff --git a/src/header.js b/src/header.js new file mode 100644 index 0000000..52ef8d3 --- /dev/null +++ b/src/header.js @@ -0,0 +1,29 @@ +/*! + * jsoneditor.js + * + * @brief + * JSONEditor is a web-based tool to view, edit, format, and validate JSON. + * It has various modes such as a tree editor, a code editor, and a plain text + * editor. + * + * Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+ + * + * @license + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * Copyright (c) 2011-2016 Jos de Jong, http://jsoneditoronline.org + * + * @author Jos de Jong, + * @version @@version + * @date @@date + */ \ No newline at end of file diff --git a/src/index.js b/src/index.js index fba61fe..d39a2db 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,8 @@ import { h, render } from 'preact' import TreeMode from './TreeMode' +import '!style!css!less!./jsoneditor.less' + /** * Factory function to create a new JSONEditor * @param container @@ -8,16 +10,11 @@ import TreeMode from './TreeMode' * @return {*} * @constructor */ -export default function jsoneditor (container, options) { +function jsoneditor (container, options) { // TODO: use JSONEditor instead of TreeMode const elem = render(h(TreeMode, {options}), container) return elem._component } -// TODO: UMD export - -window.jsoneditor = jsoneditor - - -// export JSONEditor - +// TODO: use export default jsoneditor, doesn't work out of the box in webpack +module.exports = jsoneditor; diff --git a/src/jsoneditor.css b/src/jsoneditor.less similarity index 100% rename from src/jsoneditor.css rename to src/jsoneditor.less diff --git a/src/utils/immutabilityHelpers.js b/src/utils/immutabilityHelpers.js new file mode 100644 index 0000000..62ffe82 --- /dev/null +++ b/src/utils/immutabilityHelpers.js @@ -0,0 +1,166 @@ +'use strict'; + +/** + * helper function to get a nested property in an object or array + * + * @param {Object | Array} object + * @param {Array.} path + * @return {* | undefined} Returns the field when found, or undefined when the + * path doesn't exist + */ +export function getIn (object, path) { + let value = object + let i = 0 + + while(i < path.length) { + if (Array.isArray(value) || isObject(value)) { + value = value[path[i]] + } + else { + value = undefined + } + + i++ + } + + return value +} + +/** + * helper function to replace a nested property in an object with a new value + * without mutating the object itself. + * + * Note: does not work with Arrays! + * + * @param {Object} object + * @param {Array.} path + * @param {*} value + * @return {Object} Returns a new, updated object + */ +export function setIn (object, path, value) { + if (path.length === 0) { + return value + } + + const key = path[0] + let updated + if (typeof key === 'string' && !isObject(object)) { + updated = {} + } + else if (typeof key === 'number' && !Array.isArray(object)) { + updated = [] + } + else { + updated = clone(object) + } + + updated[key] = setIn(updated[key], path.slice(1), value) + + return updated +} +/** + * helper function to replace a nested property in an object with a new value + * without mutating the object itself. + * + * @param {Object | Array} object + * @param {Array.} path + * @param {function} callback + * @return {Object | Array} Returns a new, updated object or array + */ +export function updateIn (object, path, callback) { + if (path.length === 0) { + return callback(object) + } + + const key = path[0] + let updated + if (typeof key === 'string' && !isObject(object)) { + updated = {} // change into an object + } + else if (typeof key === 'number' && !Array.isArray(object)) { + updated = [] // change into an array + } + else { + updated = clone(object) + } + + updated[key] = updateIn(updated[key], path.slice(1), callback) + + return updated +} + +/** + * helper function to delete a nested property in an object + * without mutating the object itself. + * + * @param {Object | Array} object + * @param {Array.} path + * @return {Object | Array} Returns a new, updated object or array + */ +export function deleteIn (object, path) { + if (path.length === 0) { + return object + } + + if (path.length === 1) { + const key = path[0] + const updated = clone(object) + if (Array.isArray(updated)) { + updated.splice(key, 1) + } + else { + delete updated[key] + } + + return updated + } + + const key = path[0] + const child = object[key] + if (Array.isArray(child) || isObject(child)) { + const updated = clone(object) + updated[key] = deleteIn(child, path.slice(1)) + return updated + } + else { + // child property doesn't exist. just do nothing + return object + } +} + +/** + * Flat clone the properties of an object or array + * @param {Object | Array} value + * @return {Object | Array} Returns a flat clone of the object or Array + */ +export function clone (value) { + if (Array.isArray(value)) { + return value.slice(0) + } + else if (isObject(value)) { + const cloned = {} + + Object.keys(value).forEach(key => { + cloned[key] = value[key] + }) + + return cloned + } + else { + // a primitive value + return value + } +} + +/** + * Test whether a value is an object (and not an Array or Date or primitive value) + * + * @param {*} value + * @return {boolean} + */ +export function isObject (value) { + return typeof value === 'object' && + value !== null && + !Array.isArray(value) && + value.toString() === '[object Object]' +} diff --git a/test/immutabilityHelpers.test.js b/test/immutabilityHelpers.test.js new file mode 100644 index 0000000..2149e9e --- /dev/null +++ b/test/immutabilityHelpers.test.js @@ -0,0 +1,273 @@ +import test from 'ava'; +import { getIn, setIn, updateIn, deleteIn } from '../src/utils/immutabilityHelpers' + + +test('getIn', t => { + const obj = { + a: { + b: { + c: 2 + } + }, + d: 3, + e: [ + 4, + { + f: 5, + g: 6 + } + ] + } + + t.deepEqual(getIn(obj, ['a', 'b']), {c: 2}) + t.is(getIn(obj, ['e', 1, 'f']), 5) + t.is(getIn(obj, ['e', 999, 'f']), undefined) + t.is(getIn(obj, ['non', 'existing', 'path']), undefined) +}) + +test('setIn basic', t => { + const obj = { + a: { + b: { + c: 2 + } + }, + d: 3 + } + + const updated = setIn(obj, ['a', 'b', 'c'], 4) + t.deepEqual (updated, { + a: { + b: { + c: 4 + } + }, + d: 3 + }) + + // original should be unchanged + t.deepEqual (obj, { + a: { + b: { + c: 2 + } + }, + d: 3 + }) + + t.truthy (obj !== updated) +}) + +test('setIn non existing path', t => { + const obj = {} + + const updated = setIn(obj, ['a', 'b', 'c'], 4) + + t.deepEqual (updated, { + a: { + b: { + c: 4 + } + } + }) +}) + +test('setIn replace value with object', t => { + const obj = { + a: 42, + d: 3 + } + + const updated = setIn(obj, ['a', 'b', 'c'], 4) + + t.deepEqual (updated, { + a: { + b: { + c: 4 + } + }, + d: 3 + }) +}) + +test('setIn replace value inside nested array', t => { + const obj = { + a: [ + 1, + 2, + { + b: 3, + c: 4 + } + ], + d: 5 + } + + const updated = setIn(obj, ['a', 2, 'c'], 8) + + t.deepEqual (updated, { + a: [ + 1, + 2, + { + b: 3, + c: 8 + } + ], + d: 5 + }) +}) + +test('setIn change array into object', t => { + const obj = [1,2,3] + + const updated = setIn(obj, ['foo'], 'bar') + + t.deepEqual (updated, { + foo: 'bar' + }) +}) + +test('setIn change object into array', t => { + const obj = {a:1, b:2} + + const updated = setIn(obj, [2], 'foo') + + t.deepEqual (updated, [, , 'foo']) +}) + +test('updateIn', t => { + const obj = { + a: { + b: { + c: 2 + } + }, + d: 3 + } + + const updated = updateIn(obj, ['a', 'b', 'c'], (value) => value + 100) + t.deepEqual (updated, { + a: { + b: { + c: 102 + } + }, + d: 3 + }) + + // original should be unchanged + t.deepEqual (obj, { + a: { + b: { + c: 2 + } + }, + d: 3 + }) + + t.truthy (obj !== updated) +}) + +test('updateIn (2)', t => { + const obj = { + a: { + b: { + c: 2 + } + }, + d: 3 + } + + const updated = updateIn(obj, ['a', 'b' ], (obj) => [1,2,3]) + t.deepEqual (updated, { + a: { + b: [1,2,3] + }, + d: 3 + }) +}) + +test('updateIn (3)', t => { + const obj = { + a: { + b: { + c: 2 + } + }, + d: 3 + } + + const updated = updateIn(obj, ['a', 'e' ], (value) => 'foo-' + value) + t.deepEqual (updated, { + a: { + b: { + c: 2 + }, + e: 'foo-undefined' + }, + d: 3 + }) +}) + +test('deleteIn', t => { + const obj = { + a: { + b: { + c: 2, + d: 3 + } + }, + e: 4 + } + + const updated = deleteIn(obj, ['a', 'b', 'c']) + t.deepEqual (updated, { + a: { + b: { + d: 3 + } + }, + e: 4 + }) + + // original should be unchanged + t.deepEqual (obj, { + a: { + b: { + c: 2, + d: 3 + } + }, + e: 4 + }) + + t.truthy (obj !== updated) +}) + +test('deleteIn array', t => { + const obj = { + a: { + b: [1, {c: 2, d: 3} , 4] + }, + e: 5 + } + + const updated = deleteIn(obj, ['a', 'b', 1, 'c']) + t.deepEqual (updated, { + a: { + b: [1, {d: 3} , 4] + }, + e: 5 + }) + + // original should be unchanged + t.deepEqual (obj, { + a: { + b: [1, {c: 2, d: 3} , 4] + }, + e: 5 + }) + + t.truthy (obj !== updated) +}) \ No newline at end of file diff --git a/test/util.test.js b/test/util.test.bak.js similarity index 100% rename from test/util.test.js rename to test/util.test.bak.js