    /*

    Siesta 5.3.0
    Copyright(c) 2009-2019 Bryntum AB
    https://bryntum.com/contact
    https://bryntum.com/products/siesta/license

    */
    Ext.define('Ext.ux.form.field.CodeMirror', {
        extend             : 'Ext.Component',

        alias              : 'widget.jseditor',
        alternateClassName : 'Ext.form.CodeMirror',
        requires           : [
            'Ext.tip.QuickTipManager',
            'Ext.toolbar.Item',
            'Ext.util.Format'
        ],

        width : '100%',

        /**
         * @cfg {String} mode The default mode to use when the editor is initialized. When not given, this will default to the first mode that was loaded.
         * It may be a string, which either simply names the mode or is a MIME type associated with the mode. Alternatively,
         * it may be an object containing configuration options for the mode, with a name property that names the mode
         * (for example {name: "javascript", json: true}). The demo pages for each mode contain information about what
         * configuration parameters the mode supports.
         */
        mode : 'text/javascript',

        /**
         * @cfg {Boolean} showLineNumbers Enable line numbers button in the toolbar.
         */
        showLineNumbers : true,

        /**
         * @cfg {Boolean} enableMatchBrackets Force matching-bracket-highlighting to happen
         */
        enableMatchBrackets : true,

        /**
         * @cfg {Boolean} enableElectricChars Configures whether the editor should re-indent the current line when a character is typed
         * that might change its proper indentation (only works if the mode supports indentation).
         */
        enableElectricChars : false,

        /**
         * @cfg {Boolean} enableIndentWithTabs Whether, when indenting, the first N*tabSize spaces should be replaced by N tabs.
         */
        enableIndentWithTabs : true,

        /**
         * @cfg {Boolean} enableSmartIndent Whether to use the context-sensitive indentation that the mode provides (or just indent the same as the line before).
         */
        enableSmartIndent : true,

        /**
         * @cfg {Boolean} enableLineWrapping Whether CodeMirror should scroll or wrap for long lines.
         */
        enableLineWrapping : false,

        /**
         * @cfg {Boolean} enableLineNumbers Whether to show line numbers to the left of the editor.
         */
        enableLineNumbers : true,

        /**
         * @cfg {Boolean} enableFixedGutter When enabled (off by default), this will make the gutter stay visible when the
         * document is scrolled horizontally.
         */
        enableFixedGutter : false,

        /**
         * @cfg {Number} firstLineNumber At which number to start counting lines.
         */
        firstLineNumber : 1,

        /**
         * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly.
         */
        readOnly : false,

        /**
         * @cfg {Number} pollInterval Indicates how quickly (miliseconds) CodeMirror should poll its input textarea for changes.
         * Most input is captured by events, but some things, like IME input on some browsers, doesn't generate events
         * that allow CodeMirror to properly detect it. Thus, it polls.
         */
        pollInterval : 100,

        /**
         * @cfg {Number} indentUnit How many spaces a block (whatever that means in the edited language) should be indented.
         */
        indentUnit : 4,

        /**
         * @cfg {Number} tabSize The width of a tab character.
         */
        tabSize : 4,

        /**
         * @cfg {String} theme The theme to style the editor with. You must make sure the CSS file defining the corresponding
         * .cm-s-[name] styles is loaded (see the theme directory in the distribution). The default is "default", for which
         * colors are included in codemirror.css. It is possible to use multiple theming classes at once—for example
         * "foo bar" will assign both the cm-s-foo and the cm-s-bar classes to the editor.
         */
        theme : 'default',

        scriptsLoaded : [],
        lastMode      : '',

        initComponent : function () {
            var me = this;

            //me.addEvents(
            /**
             * @event initialize
             * Fires when the editor is fully initialized (including the iframe)
             * @param {Ext.ux.form.field.CodeMirror} this
             */
            //'initialize',
            /**
             * @event activate
             * Fires when the editor is first receives the focus. Any insertion must wait
             * until after this event.
             * @param {Ext.ux.form.field.CodeMirror} this
             */
            //'activate',
            /**
             * @event deactivate
             * Fires when the editor looses the focus.
             * @param {Ext.ux.form.field.CodeMirror} this
             */
            //'deactivate',
            /**
             * @event change
             * Fires when the content of the editor is changed.
             * @param {Ext.ux.form.field.CodeMirror} this
             * @param {String} newValue New value
             * @param {String} oldValue Old value
             * @param {Array} options
             */
            //'change',

            /**
             * @event cursoractivity
             * Fires when the cursor or selection moves, or any change is made to the editor content.
             * @param {Ext.ux.form.field.CodeMirror} this
             */
            //'cursoractivity',
            /**
             * @event gutterclick
             * Fires whenever the editor gutter (the line-number area) is clicked.
             * @param {Ext.ux.form.field.CodeMirror} this
             * @param {Number} lineNumber Zero-based number of the line that was clicked
             * @param {Object} event The raw mousedown event
             */
            //'gutterclick',
            /**
             * @event scroll
             * Fires whenever the editor is scrolled.
             * @param {Ext.ux.form.field.CodeMirror} this
             */
            //'scroll',
            /**
             * @event highlightcomplete
             * Fires whenever the editor's content has been fully highlighted.
             * @param {Ext.ux.form.field.CodeMirror} this
             */
            //'highlightcomplete',
            /**
             * @event update
             * Fires whenever CodeMirror updates its DOM display.
             * @param {Ext.ux.form.field.CodeMirror} this
             */
            //'update',
            /**
             * @event keyevent
             * Fires on eery keydown, keyup, and keypress event that CodeMirror captures.
             * @param {Ext.ux.form.field.CodeMirror} this
             * @param {Object} event This key event is pretty much the raw key event, except that a stop() method is always
             * added to it. You could feed it to, for example, jQuery.Event to further normalize it. This function can inspect
             * the key event, and handle it if it wants to. It may return true to tell CodeMirror to ignore the event.
             * Be wary that, on some browsers, stopping a keydown does not stop the keypress from firing, whereas on others
             * it does. If you respond to an event, you should probably inspect its type property and only do something when
             * it is keydown (or keypress for actions that need character data).
             */
            //'keyevent'
            //);

            me.callParent(arguments);

            /*
             Fix resize issues as suggested by user koblass on the Extjs forums
             http://www.sencha.com/forum/showthread.php?167047-Ext.ux.form.field.CodeMirror-for-Ext-4.x&p=860535&viewfull=1#post860535
             */
            me.on('resize', function () {
                if (me.editor) {
                    me.editor.refresh();
                }
            }, me);

        },

        resetOriginalValue : Ext.emptyFn,

        isValid : function() {
            var errors = this.getErrors() || [];

            return errors.length === 0;
        },

        /**
         * @private override
         */
        afterRender : function () {
            var me = this;

            me.callParent(arguments);

            me.initEditor();
        },

        /**
         * @private override
         */
        initEditor : function () {
            var me = this,
                mode = 'javascript';

            me.editor = CodeMirror(me.el.dom, {
                matchBrackets       : me.enableMatchBrackets,
                electricChars       : me.enableElectricChars,
                autoClearEmptyLines : true,
                indentUnit          : me.indentUnit,
                smartIndent         : me.enableSmartIndent,
                indentWithTabs      : me.indentWithTabs,
                pollInterval        : me.pollInterval,
                lineNumbers         : me.enableLineNumbers,
                lineWrapping        : me.enableLineWrapping,
                firstLineNumber     : me.firstLineNumber,
                tabSize             : me.tabSize,
                gutters             : ["CodeMirror-lint-markers"],
                fixedGutter         : me.enableFixedGutter,
                theme               : me.theme,
                mode                : mode,
                lintWith            : CodeMirror.javascriptValidatorWithOptions({
                    "onecase"  : true,
                    "asi"      : true,
                    "expr"     : true,         // allow fn && fn()
                    "loopfunc" : true,
                    "laxbreak" : true,
                    "debug"    : true,
                    "laxcomma" : true,
                    smarttabs  : true
                }),

                onChange            : function (editor, tc) {
                    me.checkValid();
                    //me.last-child();
                    //me.fireEvent('change', me, tc.from, tc.to, tc.text, tc.next || null);
                },
                onCursorActivity    : function (editor) {
                    me.fireEvent('cursoractivity', me);
                },
                onGutterClick       : function (editor, line, event) {
                    me.fireEvent('gutterclick', me, line, event);
                },
                onFocus             : function (editor) {
                    me.fireEvent('activate', me);
                },
                onBlur              : function (editor, e) {
                    me.fireEvent('deactivate', me);
                    me.onBlur(e);
                },
                onScroll            : function (editor) {
                    me.fireEvent('scroll', me);
                },
                onHighlightComplete : function (editor) {
                    me.fireEvent('highlightcomplete', me);
                },
                onUpdate            : function (editor) {
                    me.fireEvent('update', me);
                },
                onKeyEvent          : function (editor, event) {
                    event.cancelBubble = true; // fix suggested by koblass user on Sencha forums (http://www.sencha.com/forum/showthread.php?167047-Ext.ux.form.field.CodeMirror-for-Ext-4.x&p=862029&viewfull=1#post862029)
                    me.fireEvent('keyevent', me, event);
                }

            });

            //me.editor.setValue(me.rawValue);
    //        me.setMode(me.mode);
            me.setReadOnly(me.readOnly);
            me.fireEvent('initialize', me);

            // change the codemirror css
            var css = Ext.util.CSS.getRule('.CodeMirror');
            if (css) {
                css.style.height = '100%';
                css.style.position = 'relative';
                css.style.overflow = 'hidden';
            }
            var css = Ext.util.CSS.getRule('.CodeMirror-Scroll');
            if (css) {
                css.style.height = '100%';
            }

        },

        checkValid : function() {
            var errors = this.getErrors() || [];

            if (errors.length > 0) {
                this.addCls('siesta-invalid-syntax');
            } else {
                this.removeCls('siesta-invalid-syntax');
            }
        },

        getErrors : function() {

        },

        /**
         * @private
         */
        relayBtnCmd : function (btn) {
            this.relayCmd(btn.getItemId());
        },

        /**
         * @private
         */
        relayCmd : function (cmd) {
            Ext.defer(function () {
                var me = this;
                me.editor.focus();
                switch (cmd) {
                    // auto formatting
                    case 'justifycenter':
                        if (!CodeMirror.extensions.autoIndentRange) {
                            me.loadDependencies(me.extensions.format, me.pathExtensions, me.doIndentSelection, me);
                        } else {
                            me.doIndentSelection();
                        }
                        break;

                    // line numbers
                    case 'insertorderedlist':
                        me.doChangeLineNumbers();
                        break;
                }
            }, 10, this);
        },

        doChangeLineNumbers : function () {
            var me = this;

            me.enableLineNumbers = !me.enableLineNumbers;
            me.editor.setOption('lineNumbers', me.enableLineNumbers);
        },

        /**
         * @private
         */
        doIndentSelection : function () {
            var me = this;

            me.reloadExtentions();

            try {
                var range = { from : me.editor.getCursor(true), to : me.editor.getCursor(false) };
                me.editor.autoIndentRange(range.from, range.to);
            } catch (err) {
            }
        },

        /**
         * Set the editor as read only
         *
         * @param {Boolean} readOnly
         */
        setReadOnly : function (readOnly) {
            var me = this;

            if (me.editor) {
                me.editor.setOption('readOnly', readOnly);
            }
        },

        onDisable : function () {
            this.bodyEl.mask();
            this.callParent(arguments);
        },

        onEnable : function () {
            this.bodyEl.unmask();
            this.callParent(arguments);
        },

        /**
         * Sets a data value into the field and runs the change detection.
         * @param {Mixed} value The value to set
         * @return {Ext.ux.form.field.CodeMirror} this
         */
        setValue : function (value) {
            value = value || '';

            var me = this;
    //        me.mixins.field.setValue.call(me, value);
    //        me.rawValue = value;
            if (me.editor) {
                me.editor.setValue(value);
            }
            return me;
        },

        /**
         * Return submit value to the owner form.
         * @return {Mixed} The field value
         */
        getSubmitValue : function () {
            var me = this;
            return me.getValue();
        },

        getRawValue : function () {
            return this.getValue();
        },

        /**
         * Return the value of the CodeMirror editor
         * @return {Mixed} The field value
         */
        getValue : function () {
            var me = this;

            if (me.editor)
                return me.editor.getValue() || '';
            else
                return '';
        },

        /**
         * @private
         */
        onDestroy : function () {
            var me = this;
            if (me.rendered) {

                for (var prop in me.editor) {
                    if (me.editor.hasOwnProperty(prop)) {
                        delete me.editor[prop];
                    }
                }
                Ext.destroyMembers('tb', 'toolbarWrap', 'editorEl');
            }
            me.callParent();
        },

        commentLine : function () {
            var editor = this.editor;
            var start = editor.getCursor(true);
            var end = editor.getCursor(false);
            var currentLine = start.line;
            var doFormat = false;

            function toggleCommented(line) {
                text = editor.getLine(line);
                if (text.match(/^\s*\/\/\s*/)) {
                    text = text.replace(/\s*\/\/\s*/, '');
                    doFormat = true;
                } else {
                    text = text.substr(0, text.indexOf(Ext.String.trim(text))) + '// ' + Ext.String.trim(text);
                }
                editor.setLine(line, text);
            }

            if (start.line === end.line && start.ch === end.ch) {
                toggleCommented(start.line);
            } else if (start.character == 0) {
                while (currentLine != end.line) {
                    toggleCommented(currentLine);
                    currentLine = editor.getLine(++currentLine);
                }
                editor.selectLines(start.line, start.ch, end.line, end.ch);
            } else {
                var text = editor.getSelection();
                var diff = 1;
                if (text.match(/^\/\*/) && text.match(/^\/\*/)) {
                    text = text.substring(2, text.length - 2);
                    diff = -1;
                    doFormat = true;
                } else {
                    text = "/*" + text + "*/";
                }
                editor.replaceSelection(text);

                // following line crash
    //            if (start.line == end.line) {
    //                editor.setSelection(start.line, start.ch, end.line, end.ch+(diff*4));
    //            } else {
    //                editor.setSelection(start.line, start.ch, end.line, end.ch+(diff*2));
    //            }
            }

            if (doFormat) {
                editor.autoIndentRange({ line : 0 }, { line : editor.lineCount() });
            }
        }

    });
    //
    //(function () {
    //    CodeMirror.extendMode("css", {
    //        commentStart      : "/*",
    //        commentEnd        : "*/",
    //        newlineAfterToken : function (type, content) {
    //            return /^[;{}]$/.test(content);
    //        }
    //    });
    //
    //    CodeMirror.extendMode("javascript", {
    //        commentStart      : "/*",
    //        commentEnd        : "*/",
    //        // FIXME semicolons inside of for
    //        newlineAfterToken : function (type, content, textAfter, state) {
    //            if (this.jsonMode) {
    //                return /^[\[,{]$/.test(content) || /^}/.test(textAfter);
    //            } else {
    //                if (content == ";" && state.lexical && state.lexical.type == ")") return false;
    //                return /^[;{}]$/.test(content) && !/^;/.test(textAfter);
    //            }
    //        }
    //    });
    //
    //    CodeMirror.extendMode("xml", {
    //        commentStart      : "<!--",
    //        commentEnd        : "-->",
    //        newlineAfterToken : function (type, content, textAfter) {
    //            return type == "tag" && />$/.test(content) || /^</.test(textAfter);
    //        }
    //    });
    //
    //    // Comment/uncomment the specified range
    //    CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
    //        var cm = this, curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode;
    //        cm.operation(function () {
    //            if (isComment) { // Comment range
    //                cm.replaceRange(curMode.commentEnd, to);
    //                cm.replaceRange(curMode.commentStart, from);
    //                if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
    //                    cm.setCursor(from.line, from.ch + curMode.commentStart.length);
    //            } else { // Uncomment range
    //                var selText = cm.getRange(from, to);
    //                var startIndex = selText.indexOf(curMode.commentStart);
    //                var endIndex = selText.lastIndexOf(curMode.commentEnd);
    //                if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
    //                    // Take string till comment start
    //                    selText = selText.substr(0, startIndex)
    //                        // From comment start till comment end
    //                        + selText.substring(startIndex + curMode.commentStart.length, endIndex)
    //                        // From comment end till string end
    //                        + selText.substr(endIndex + curMode.commentEnd.length);
    //                }
    //                cm.replaceRange(selText, from, to);
    //            }
    //        });
    //    });
    //
    //    // Applies automatic mode-aware indentation to the specified range
    //    CodeMirror.defineExtension("autoIndentRange", function (from, to) {
    //        var cmInstance = this;
    //        this.operation(function () {
    //            for (var i = from.line; i <= to.line; i++) {
    //                cmInstance.indentLine(i, "smart");
    //            }
    //        });
    //    });
    //
    //    // Applies automatic formatting to the specified range
    //    CodeMirror.defineExtension("autoFormatRange", function (from, to) {
    //        var cm = this;
    //        var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
    //        var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
    //        var tabSize = cm.getOption("tabSize");
    //
    //        var out = "", lines = 0, atSol = from.ch == 0;
    //
    //        function newline() {
    //            out += "\n";
    //            atSol = true;
    //            ++lines;
    //        }
    //
    //        for (var i = 0; i < text.length; ++i) {
    //            var stream = new CodeMirror.StringStream(text[i], tabSize);
    //            while (!stream.eol()) {
    //                var inner = CodeMirror.innerMode(outer, state);
    //                var style = outer.token(stream, state), cur = stream.current();
    //                stream.start = stream.pos;
    //                if (!atSol || /\S/.test(cur)) {
    //                    out += cur;
    //                    atSol = false;
    //                }
    //                if (!atSol && inner.mode.newlineAfterToken &&
    //                    inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i + 1] || "", inner.state))
    //                    newline();
    //            }
    //            if (!stream.pos && outer.blankLine) outer.blankLine(state);
    //            if (!atSol) newline();
    //        }
    //
    //        cm.operation(function () {
    //            cm.replaceRange(out, from, to);
    //            for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
    //                cm.indentLine(cur, "smart");
    //            cm.setSelection(from, cm.getCursor(false));
    //        });
    //    });
    //})();