1. #1
    Sencha User
    Join Date
    Oct 2007
    Posts
    8
    Vote Rating
    -1
    qulys is on a distinguished road

      0  

    Lightbulb Ext.ux.panel.CodeMirror

    Ext.ux.panel.CodeMirror


    Hi,

    This is my CodeMirror implementation.
    It extends panel, so you can have a toolbar.
    It has JSLINT included so you can verify your code by pressing a button.
    Also I have modified original themes to use a dark color aproach.
    It only works with codeMirror 0.63 , higher versions are buggy...

    Extension code:
    HTML Code:
    /*global Ext,  JSLINT, CodeMirror  */
    
    /**
     * @class Ext.ux.panel.CodeMirror
     * @extends Ext.Panel
     * Converts a panel into a code mirror editor with toolbar
     * @constructor
     * 
     * @author Dan Ungureanu - ungureanu.web@gmail.com / http://www.devweb.ro
     * @version 0.1
     */
    
     // Define a set of code type configurations
    Ext.ns('Ext.ux.panel.CodeMirrorConfig');
    Ext.apply(Ext.ux.panel.CodeMirrorConfig, {
        cssPath: "CodeMirror-0.63/css/",
        jsPath: "CodeMirror-0.63/js/"
    });
    Ext.apply(Ext.ux.panel.CodeMirrorConfig, {
        parser: {
            defo: { // js code
                parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
                stylesheet: Ext.ux.panel.CodeMirrorConfig.cssPath + "jscolors.css"
            },
            css: {
                parserfile: ["parsecss.js"],
                stylesheet: Ext.ux.panel.CodeMirrorConfig.cssPath + "csscolors.css"
            },
            js: {
                parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
                stylesheet: Ext.ux.panel.CodeMirrorConfig.cssPath + "jscolors.css"
            },
            php: {
                parserfile: ["tokenizephp.js", "parsephp.js"],
                stylesheet: Ext.ux.panel.CodeMirrorConfig.cssPath + "phpcolors.css"
            },
            html: {
                parserfile: ["parsexml.js", "parsecss.js", "tokenizejavascript.js", "parsejavascript.js", "tokenizephp.js", "parsephp.js", "parsephphtmlmixed.js"],
                stylesheet: [Ext.ux.panel.CodeMirrorConfig.cssPath + "xmlcolors.css", Ext.ux.panel.CodeMirrorConfig.cssPath + "jscolors.css", Ext.ux.panel.CodeMirrorConfig.cssPath + "csscolors.css", Ext.ux.panel.CodeMirrorConfig.cssPath + "phpcolors.css"]
                
            },
            mixed: {
                parserfile: ["parsexml.js", "parsecss.js", "tokenizejavascript.js", "parsejavascript.js", "tokenizephp.js", "parsephp.js", "parsephphtmlmixed.js"],
                stylesheet: [Ext.ux.panel.CodeMirrorConfig.cssPath + "xmlcolors.css", Ext.ux.panel.CodeMirrorConfig.cssPath + "jscolors.css", Ext.ux.panel.CodeMirrorConfig.cssPath + "csscolors.css", Ext.ux.panel.CodeMirrorConfig.cssPath + "phpcolors.css"]
                
            }
        }
    });
    
    Ext.ns('Ext.ux.panel.CodeMirror');
    Ext.ux.panel.CodeMirror = Ext.extend(Ext.Panel, {
        
    
        sourceCode: '/* Default code */',
        initComponent: function() {
            // this property is used to determine if the source content changes
            this.contentChanged = false;
            var oThis = this;
            this.debugWindow = new Ext.Window({
                title: 'Debug',
                width: 500,
                layout: 'border',
                closeAction: 'hide',
                height: 160,
                items: [new Ext.grid.GridPanel({
                    layout: 'fit',
                    region: 'center',
                    border: false,
                    listeners: {
                        rowclick: function(grid) {
                            var oData = grid.getSelectionModel().getSelected().data;
                            oThis.codeMirrorEditor.jumpToLine(oData.line);
                        }
                    },
                    store: new Ext.data.ArrayStore({
                        fields: [{
                            name: 'line'
                        }, {
                            name: 'character'
                        }, {
                            name: 'reason'
                        }]
                    }),
                    columns: [{
                        id: 'line',
                        header: 'Line',
                        width: 60,
                        sortable: true,
                        dataIndex: 'line'
                    }, {
                        id: 'character',
                        header: 'Character',
                        width: 60,
                        sortable: true,
                        dataIndex: 'character'
                    }, {
                        header: 'Description',
                        width: 240,
                        sortable: true,
                        dataIndex: 'reason'
                    }],
                    stripeRows: true
                })]
            });
            
            Ext.apply(this, {
                items: [{
                    xtype: 'textarea',
                    readOnly: false,
                    hidden: true,
                    value: this.sourceCode
                }],
                tbar: [{
                    text: 'Save',
                    handler: this.triggerOnSave,
                    scope: this
                }, {
                    text: 'Undo',
                    handler: function() {
                        this.codeMirrorEditor.undo();
                    },
                    scope: this
                }, {
                    text: 'Redo',
                    handler: function() {
                        this.codeMirrorEditor.redo();
                    },
                    scope: this
                }, {
                    text: 'Indent',
                    handler: function() {
                        this.codeMirrorEditor.reindent();
                    },
                    scope: this
                }, {
                    itemId: 'spellChecker',
                    disabled: true,
                    text: 'JS Lint',
                    handler: function() {
                        try {
                            var bValidates = JSLINT(this.findByType('textarea')[0].getValue());
                            
                            var oStore = this.debugWindow.findByType('grid')[0].getStore();
                            if (!bValidates) {
                                var aErrorData = [];
                                
                                for (var err in JSLINT.errors) {
                                    if (JSLINT.errors.hasOwnProperty(err) && (JSLINT.errors[err] !== null)) {
                                        aErrorData.push([JSLINT.errors[err].line, JSLINT.errors[err].character, JSLINT.errors[err].reason]);
                                    }
                                }
                                
                                oStore.loadData(aErrorData, false);
                                this.debugWindow.show();
                                
                            }
                            else {
                            
                                oStore.loadData([[1, 1, 'Congratulation! No errors found.']], false);
                                this.debugWindow.show();
                            }
                        }catch(e){}
                        
                    },
                    scope: this
                }]
            });
            
            Ext.ux.panel.CodeMirror.superclass.initComponent.apply(this, arguments);
        },
        
        triggerOnSave: function(){
            this.setTitleClass(true);
            var sNewCode = this.codeMirrorEditor.getCode();
            
            Ext.state.Manager.set("edcmr_"+this.itemId+'_lnmbr', this.codeMirrorEditor.currentLine());
            
            this.oldSourceCode = sNewCode;
            this.onSave(arguments[0] || false);
        },
        
        onRender: function() {
            this.oldSourceCode = this.sourceCode;
            Ext.ux.panel.CodeMirror.superclass.onRender.apply(this, arguments);
            // trigger editor on afterlayout
            this.on('afterlayout', this.triggerCodeEditor, this, {
                single: true
            });
            
        },
        
        /** @private */
        triggerCodeEditor: function() {
            //this.codeMirrorEditor;
            var oThis = this;
            var oCmp = this.findByType('textarea')[0];
            var editorConfig = Ext.applyIf(this.codeMirror || {}, {
               height: "100%",
               width: "100%",
               lineNumbers: true,
               textWrapping: false,
               content: oCmp.getValue(),
               indentUnit: 4,
               tabMode: 'shift',
               readOnly: oCmp.readOnly,
               path: Ext.ux.panel.CodeMirrorConfig.jsPath,
               autoMatchParens: true,
               initCallback: function(editor) {
                   editor.win.document.body.lastChild.scrollIntoView();
                   try {
                       var iLineNmbr = ((Ext.state.Manager.get("edcmr_" + oThis.itemId + '_lnmbr') !== undefined) ? Ext.state.Manager.get("edcmr_" + oThis.itemId + '_lnmbr') : 1);
                       //console.log(iLineNmbr);
                       editor.jumpToLine(iLineNmbr);
                   }catch(e){
                       //console.error(e);
                   }
               },
               onChange: function() {
                   var sCode = oThis.codeMirrorEditor.getCode();
                   oCmp.setValue(sCode);
                   
                   if(oThis.oldSourceCode == sCode){
                       oThis.setTitleClass(true);
                   }else{
                       oThis.setTitleClass();
                   }
                   
               }
           });
            
            var sParserType = oThis.parser || 'defo';
            editorConfig = Ext.applyIf(editorConfig, Ext.ux.panel.CodeMirrorConfig.parser[sParserType]);
            
            this.codeMirrorEditor = new CodeMirror.fromTextArea( Ext.getDom(oCmp.id).id, editorConfig);
            
            // Disable spell check button for non-js content
            if (sParserType == 'js' || sParserType == 'css') {
                this.getTopToolbar().getComponent('spellChecker').enable();
            }
        },
        
        setTitleClass: function(){
            //var tabEl = Ext.get(this.ownerCt.getTabEl( this ));
            if(arguments[0] === true){// remove class
                //tabEl.removeClass( "tab-changes" );
                this.contentChanged = false;
            }else{//add class
                //tabEl.addClass( "tab-changes" );
                this.contentChanged = true;
            }
        }
    });
    
    
    Ext.reg('uxCodeMirrorPanel', Ext.ux.panel.CodeMirror);
    Example:
    HTML Code:
    {
            xtype: 'uxCodeMirrorPanel',
            title: 'JS example',
            closable: true,
            listeners: {
                render: function(){
                    this.doLayout();
                }
            },
            sourceCode: '/* paste here somme js code */',
            //layout: 'fit',
            parser: 'js',
            onSave: function() {
                // save logic here
                // this.codeMirrorEditor gets you access to original code mirror object :)
            },
            codeMirror: {
                height: '100%',
                width: '100%'
            }
        }
    I also attached a working example to unpack and drop into your ext30 examples folder.
    Attached Images
    Attached Files

  2. #2
    Sencha - Architect Dev Team aconran's Avatar
    Join Date
    Mar 2007
    Posts
    9,198
    Vote Rating
    120
    aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold

      0  

    Default


    Nice work, this is a neat ux.
    Aaron Conran
    @aconran
    Sencha Architect Development Team

  3. #3
    Ext User
    Join Date
    Mar 2010
    Posts
    8
    Vote Rating
    0
    knowledgeVirtue is on a distinguished road

      0  

    Default


    Hi,

    I'm having issue populating value to codemirror editor. Based on combox value i have to fill codemirror editor. But by using Ext.getCmp('codemirrorid').setValue(x) or Ext.getCmp('codemirrorid').setCode(x) doesn't work. Can you help me how to populate the editor?

  4. #4
    Sencha User wemerson.januario's Avatar
    Join Date
    Nov 2008
    Location
    Brazil, Goias, Goiânia
    Posts
    434
    Vote Rating
    6
    wemerson.januario is on a distinguished road

      0  

    Default


    nice work. I will test
    Wemerson Januario
    Twitter:
    @wemersonjanuar
    Skype:
    wemerson.januario
    Email: wemerson.januario@gmail.com
    Fone:
    62 9106-6689
    From: Goiânia, Brazil
    Desenvolvedor ExtJS/ ExtJS Developer

  5. #5
    Sencha User asp3ctus's Avatar
    Join Date
    Jan 2010
    Location
    Russia
    Posts
    28
    Vote Rating
    0
    asp3ctus is on a distinguished road

      0  

    Default


    What kinda bugs did you find with a later version of CodeMirror ?
    I have tried git version ... and to me it worked the same .....
    But i din't test it much

    Thanks, great stuff!!

  6. #6
    Ext User
    Join Date
    May 2010
    Posts
    2
    Vote Rating
    0
    revresxunil is on a distinguished road

      0  

    Default


    Great plugin!! Works great for my research based web portal project.

    I had initial problems with the location in which I uploaded the files but modifying the plugin file with exact locations of codemirror fixed it.

    Long live open source.

    Mike

  7. #7
    Ext JS Premium Member prophet's Avatar
    Join Date
    Mar 2007
    Location
    Greenwich, CT
    Posts
    187
    Vote Rating
    0
    prophet is on a distinguished road

      0  

    Default


    Very nice! Thank you!

    Question: Is there a way to make the editor take up the rest of the empty vertical space in the window?
    Even though the height is set to 100%, it seems to default to 150px -- I can make it larger by giving it an exact pixel value, but I'd rather keep the re-size functionality of the containing window.
    Brad Baumann

  8. #8
    Sencha - Services Team Stju's Avatar
    Join Date
    Dec 2008
    Location
    Redwood city, California
    Posts
    288
    Vote Rating
    3
    Stju is on a distinguished road

      0  

    Default


    Nice component!
    Any way to paste some code dynamically at cursor position in already rendered CodeMirror panel?

  9. #9
    Ext JS Premium Member prophet's Avatar
    Join Date
    Mar 2007
    Location
    Greenwich, CT
    Posts
    187
    Vote Rating
    0
    prophet is on a distinguished road

      0  

    Default


    Check out the methods exposed in the codeEditor object:
    Ext.getCmp('CodeMirrorCmpID').codeEditor
    Brad Baumann

  10. #10
    Sencha - Services Team Stju's Avatar
    Join Date
    Dec 2008
    Location
    Redwood city, California
    Posts
    288
    Vote Rating
    3
    Stju is on a distinguished road

      0  

    Default


    Had today time to work around this component..
    so here are few additional methods:
    Code:
        getValue: function() {
            return this.codeMirrorEditor.getCode();
        },
    
        setValue: function(text) {
            this.codeMirrorEditor.setCode(text);
        },
    
        setValueAtCursor: function(text) {
            var cursorPosition = this.codeMirrorEditor.cursorPosition();
            var handleForCursorLine = this.codeMirrorEditor.cursorLine();
            this.codeMirrorEditor.insertIntoLine(handleForCursorLine, cursorPosition.character, text);
        }