PDA

View Full Version : Ext.ux.panel.CodeMirror



qulys
15 Jan 2010, 4:03 AM
Hi,

This is my CodeMirror (http://marijn.haverbeke.nl/codemirror/) implementation.
It extends panel, so you can have a toolbar.
It has JSLINT (http://www.jslint.com/) 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:

/*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:

{
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.

aconran
19 Jan 2010, 6:01 PM
Nice work, this is a neat ux.

knowledgeVirtue
20 Apr 2010, 1:00 PM
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?

wemerson.januario
20 Apr 2010, 6:21 PM
nice work. I will test

asp3ctus
26 Apr 2010, 8:48 PM
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!!

revresxunil
4 May 2010, 8:16 AM
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

prophet
25 Jun 2010, 9:16 AM
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.

Stju
12 Jul 2010, 8:41 AM
Nice component!
Any way to paste some code dynamically at cursor position in already rendered CodeMirror panel?

prophet
12 Jul 2010, 9:12 AM
Check out the methods exposed in the codeEditor object:
Ext.getCmp('CodeMirrorCmpID').codeEditor

Stju
13 Jul 2010, 11:18 PM
Had today time to work around this component..
so here are few additional methods:


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);
}

prophet
14 Jul 2010, 6:09 AM
Nice additions, Stju!
...Dont't getValue and setValue already exist though?

saprot
29 Jul 2010, 11:07 PM
it works with the latest CodeMirror git snapshot :)

talha06
16 Nov 2010, 1:35 PM
Had today time to work around this component..
so here are few additional methods:


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);
}

It doesn't work.. Gives an error like this : "this.editor is undefined".. Can someone help me? (I mean setValue and getValue functions)

Stju
17 Nov 2010, 3:18 AM
It works, it has been tested with different CodeMirror versions!
As You didn't post the line and file where that error appears it's almost impossible to help, although it looks like You have not included codemirror file before extjs code..

talha06
17 Nov 2010, 3:26 AM
It works, it has been tested with different CodeMirror versions!
As You didn't post the line and file where that error appears it's almost impossible to help, although it looks like You have not included codemirror file before extjs code..
Error happened at this line:

setValue: function(text) {
this.codeMirrorEditor.setCode(text);
}

And here's the list of my includes:

<script src="./script/jsBeautify/beautify.js"></script>
<script src="./script/lib/ext/ux/codemirror/CodeMirror-0.63/js/codemirror.js"></script>
<script src="./script/lib/ext/ux/codemirror/Ext.ux.panel.CodeMirror.js"></script>
<script src="./script/lib/ext/ux/codemirror/jslint.js"></script>

<!-- Source File -->
<script type="text/javascript" src="./script/homepage.js"></script>

Stju
17 Nov 2010, 3:50 AM
1. You are using VERY old version of editor.. I strongly recommend using latest one, since the some latest versions fixes numerous Webkit browser related issues!
2.Editor must be rendered before You can set the its value!
Excerpt from my code, maybe it will help You:


this.snipCodePanel = new Ext.ux.panel.CodeMirror({
height:240,
title:'Code view',
autoScroll:true,
bodyStyle:'background-color:white',
//codemirror stuff
listeners: {
render: function(){
this.doLayout();
}
},
//sourceCode: '',
parser: 'php',
codeMirror:{
lineNumbers:false
},
...
});

talha06
17 Nov 2010, 5:36 AM
OK, thanks a lot for your helps Stju. It works now, I thought the attached file is recent; but the recent version is 0.91. (Release date : 11/11/2010)

Stju
17 Nov 2010, 5:37 AM
Welcome!
Nice to hear!

Zerogiven
23 Nov 2010, 1:18 PM
hi,

first, thanks for your panel it works a bit better for me than the other codemirror ux. I also made two functions for hide/show codemirror and hide/show the textarea with the same value.

Maybe someone else can need it too :)


codeMirrorHidden: false, //just a public config
hideCodeMirror: function () {
var iframeEl = Ext.select('div#'+this.id + ' > div > div > .CodeMirror-wrapping', true);
var textareaEl = Ext.select('div#'+this.id + ' > div > div > textarea', true);

this.items.itemAt(0).setValue( this.getValue() );

iframeEl.setVisible(false);
textareaEl.setVisible(true);
textareaEl.removeClass('x-hide-display');

this.items.itemAt(0).setWidth("100%");
this.items.itemAt(0).setHeight("100%");

this.codeMirrorHidden = true;
},
showCodeMirror: function () {
var iframeEl = Ext.select('div#'+this.id + ' > div > div > .CodeMirror-wrapping', true);
var textareaEl = Ext.select('div#'+this.id + ' > div > div >textarea', true);

this.setValue( this.items.itemAt(0).getValue() );

textareaEl.addClass('x-hide-display');
textareaEl.setVisible(false);
iframeEl.setVisible(true);

this.codeMirrorHidden = false;
}At least i inserted the following code insert the triggerCodeEditor function right after initializing codemirror (new CodeMirror...):


var iframeEl = Ext.select('div#'+this.id + ' > div > div > .CodeMirror-wrapping', true);
var textareaEl = Ext.select('div#'+this.id + ' > div > div > textarea', true);

textareaEl.setVisibilityMode(Ext.Element.DISPLAY);
iframeEl.setVisibilityMode(Ext.Element.DISPLAY);

if (this.codeMirrorHidden) this.hideCodeMirror();

greetz
Chris

bugu1986
3 Dec 2010, 7:47 AM
nice work

chesstrix
7 Feb 2011, 3:02 PM
Works like a charm.
Thank you for posting.

steffenk
10 Feb 2011, 2:32 PM
I implemented codemirror in TYPO3 with ExtJS and built a fileeditor which highlights all kinds of files. Integrated jslint, versioning, and much more.

It works great with latest codemirror. If you are interested here is the main file:

http://forge.typo3.org/projects/typo3v4-em/repository/entry/trunk/res/js/em_files.js

and it looks like this:

http://forge.typo3.org/attachments/download/5450

steffenk
11 Feb 2011, 1:50 AM
parser: 'php',


this won't work. parser is the class name of the parser you use and included.
Also raw phpParser won't work because the <? let's break the parsing.

This way it will work:


parser: 'PHPHTMLMixedParser',
parserfile: [
"parsexml.js",
"parsecss.js",
"tokenizejavascript.js",
"parsejavascript.js",
"../contrib/php/js/tokenizephp.js",
"../contrib/php/js/parsephp.js",
"../contrib/php/js/parsephphtmlmixed.js",
"../contrib/sql/js/parsesql.js"
],

Stju
11 Feb 2011, 2:23 AM
this won't work.

What do You mean with won't work? It will work if You don't use <?php, so for plain php it's working correctly. If You want to use it combined mode for php and html, there is already mixed class defined and it works like a charm :) But be aware then in this case if You don't use <?php it won't highlight! So in general it depends on every other user case ;) Here are the samples:
24661
24662
24663
24664

steffenk
11 Feb 2011, 3:12 AM
Hi,

yes, exactly this is the problem with the parser that it depends of having php tag or not ;)
I wrote this because i had errors using PHPParser with <?php ...

I wondered about parser: 'php', as i always use the correct classnames:
PHPParser, PHPHTMLMixedParser

With my debugs, all other declarations didn't worked. Unfortunally there is no try/catch, so wrong parser name direct throws js error.

btw - the author works on codemirror2 which is a complete rewrite with much cleaner code, i'm looking forward. Existing codemirror has some weakness.

Stju
11 Feb 2011, 3:18 AM
Hi!
Yes, already gave some hands over first beta :) BTW wanted to say the same \:D/ Let's keep fingers crossed! =;

khebs@live.com
13 Feb 2011, 6:07 PM
I havent created a UX for this yet, since, i only used this for just 1 window.. but, this is what i did...




var form = new Ext.form.FormPanel({
...
items: [{
xtype: 'textarea',
name: 'addjson'
...
}]
...
});

var textarea = form.getForm().findField('addjson');

jsonfield = CodeMirror.fromTextArea(textarea.el.dom, {
height: textarea.getHeight() + 'px',
content: textarea.getValue(),
parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
stylesheet: __baseurl + "lib/codemirror/css/jscolors.css",
path: __baseurl + "lib/codemirror/js/",
autoMatchParens: true,
textWrapping: false,
width: textarea.getWidth(),
parserConfig: {
json: true
}
});

// Just to remove the invalid marker
//
jsonfield.frame.contentWindow.addEventListener('keydown', function(e){
jsonfield.wrapping.className = 'x-form-text x-form-field no-margin-field';
});

jsonfield.wrapping.className = 'x-form-text x-form-field no-margin-field';

SnakeMedia
16 Feb 2011, 2:35 PM
Hello @qulys,

good work and keep your development for nice application ^^!

1. Question: Can you update with preview?
I give a nice suggestion for preview per html site and support with javascript frameworks like Extjs or Qooxdoo and any frameworks of js to preview...

2. Question: Paint-able function like xur framework or any function with css-style?
Example: I create an shape quadrat with border than i save new image / sprite into local drive.
Do you believe posible via paint function without flash application only JS-Framework?

Thanks for new component, awesome! ^^

bobmanc
4 Apr 2011, 11:15 AM
i know it is early yet but is there an ExtJS v4 version in the works?

vlx
27 May 2011, 2:19 AM
Hi,

I have made a port to ExjJS 4.0 and CodeMirror 2.0, hope someone can use it.

26301

SnakeMedia
15 Sep 2011, 2:13 PM
Thanks for updated version ^^! I like this. Did you code with Preview like HTML Design or Source ? WTF You are faster than my question ^^ :P http://codemirror.net/demo/preview.html

Thanks :) Keep working! Live long!

mshepherd
18 Oct 2011, 8:58 AM
when you have text the overflows horizontally and you click on the scroll bar it jumps to the far right and selects text in the code mirror object when you let go of scrolling and move the mouse up. Any ideas how to fix this?

bkraut
7 Sep 2012, 12:46 AM
Hi,
I'm having a lot of problems with height of the CodeMirror component. No matter what I do I can not achieve that the component fills the whole Panel.

Here is my code:



var codeMirrorPanel = new Ext.ux.panel.CodeMirror({
id: 'CodeMirrorEditor',
sourceCode: '/* Write config here */',
layout: 'fit',
parser: 'js',
border: false,
onSave: function() {
var configField = Ext.getCmp("config");
var code = this.codeMirrorEditor.getCode();
configField.setValue(code);
},
codeMirror: { height: "100%", width: '100%' }
});


height: "100%" doesn't work. How can I achieve that the component will fill the whole panel?

qulys
10 Dec 2012, 1:52 PM
Hi,

I haven't been much around lately, but here are som updates(CodeMirror 2.32):

First Ext.ux.form.CodeMirror form field:



Ext.namespace('Ext.ux.form');

Ext.ux.form.CodeMirror = Ext.extend(Ext.form.TextArea, {

version: '1.1.0',
versionDate: '30.07.2012',
language: 'mixed',

initComponent: function() {

Ext.ux.form.CodeMirror.superclass.initComponent.apply(this, arguments);
this.addEvents('initialize');

},

triggerCodeEditor: function(){

var
mode,
hlLine,
foldFunc = CodeMirror.newFoldFunction(CodeMirror.braceRangeFinder),
me = this;

switch (me.language.toLowerCase()) {
case 'css':
mode = "text/css";
break;

case 'js':
mode = "text/javascript";
break;

case 'php':
mode = "text/x-php";
break;

case 'mixed':
mode = "application/x-httpd-php";
foldFunc = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder);
break;

default:
mode = "application/x-httpd-php";
foldFunc = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder);
break;
}

me.codeEditor = CodeMirror.fromTextArea(document.getElementById(me.id), {
lineNumbers: true,
theme: 'cobalt',
mode: mode,
tabSize: 4,
indentUnit: 4,
onCursorActivity: function() {
me.codeEditor.matchHighlight("CodeMirror-matchhighlight");
me.codeEditor.setLineClass(hlLine, null, null);
hlLine = me.codeEditor.setLineClass(me.codeEditor.getCursor().line, null, "xcme-activeline");
},
onChange: function(){
me.setRawValue(me.codeEditor.getValue());
},
onGutterClick: foldFunc,
extraKeys: {"Ctrl-Q": function(cm){foldFunc(cm, cm.getCursor().line);}}
});

hlLine = me.codeEditor.setLineClass(0, "xcme-activeline");

me.on({
resize: function(ta, width, height) {
width -= 35;
me.codeEditor.setSize(width, height);
}
});

},

setValue: function(v) {

if(this.rendered){
this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
this.validate();
if (Ext.isDefined(this.codeEditor)) {
this.codeEditor.setValue(v);
this.value = v;
}
}
return this;
},

onRender: function(){
Ext.ux.form.CodeMirror.superclass.onRender.apply(this, arguments);

this.on('afterrender', this.triggerCodeEditor, this, {
single: true
});

}
});
Ext.reg('ux-codemirror', Ext.ux.form.CodeMirror);


and CodeMirror Panel extension(featuring seach plugin, code folding, line highlighting, undo, redo, JSLINT):



/*global Ext, Easy, JSLINT, CodeMirror */


Ext.ns('Easy.CodeMirror');
Easy.CodeMirror = Ext.extend(Ext.Panel, {

sourceCode: '/* Easy Road empty source code file */',
initComponent: function() {
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.setCursor(oData.line);
}
},
store: new Ext.data.ArrayStore({
fields: [{
name: 'line'
}, {
name: 'character'
}, {
name: 'reason'
}]
}),
columns: [{
id: 'line',
header: Easy.lang.CodeMirror.debugGridColLine,
width: 60,
sortable: true,
dataIndex: 'line'
}, {
id: 'character',
header: Easy.lang.CodeMirror.debugGridColChar,
width: 60,
sortable: true,
dataIndex: 'character'
}, {
header: Easy.lang.CodeMirror.debugGridColReason,
width: 240,
sortable: true,
dataIndex: 'reason'
}],
stripeRows: true
})]
});


Ext.apply(this, {
items: [{
xtype: 'textarea',
readOnly: false,
hidden: true,
value: this.sourceCode
}],
tbar: [{
iconCls: 'icon-fugue-disk',
tooltip: 'Save',
handler: this.triggerOnSave,
scope: this
}, {
iconCls: 'icon-arrow-undo',
tooltip: 'Undo',
handler: function() {
this.codeMirrorEditor.undo();
},
scope: this
}, {
iconCls: 'icon-arrow-redo',
tooltip: 'Redo',
handler: function() {
this.codeMirrorEditor.redo();
},
scope: this
}, {
itemId: 'spellChecker',
disabled: true,
iconCls: 'icon-fugue-spell-check',
tooltip: 'JS Lint',
handler: this.validateJSLINT,
scope: this
}]
});

Easy.CodeMirror.superclass.initComponent.apply(this, arguments);
},

validateJSLINT: function(){
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{
Easy.showInfo('JSLINT says: <br />Congratulation! No errors found.');
}
},

triggerOnSave: function(){
oThis = this;
var sParserType = oThis.parser || 'defo';

oThis.setTitleClass(true);
var sNewCode = oThis.codeMirrorEditor.getValue();

oThis.oldSourceCode = sNewCode;
oThis.onSave(arguments[0] || false);
},

onRender: function() {
this.oldSourceCode = this.sourceCode;
Easy.CodeMirror.superclass.onRender.apply(this, arguments);
// trigger editor on afterlayout
this.on('afterlayout', this.triggerCodeEditor, this, {
single: true
});

},

/** @private */
triggerCodeEditor: function() {
var
mode,
hlLine,
foldFunc = CodeMirror.newFoldFunction(CodeMirror.braceRangeFinder),
me = this;

switch (me.parser.toLowerCase()) {
case 'css':
mode = "text/css";
break;

case 'js':
mode = "text/javascript";
break;

case 'php':
mode = "text/x-php";
break;

case 'html':
mode = "application/x-httpd-php";
foldFunc = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder);
break;

case 'mixed':
mode = "application/x-httpd-php";
foldFunc = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder);
break;

default:
mode = "application/x-httpd-php";
foldFunc = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder);
break;
}
var oThis = this;
var oCmp = this.findByType('textarea')[0];

var editorConfig = Ext.applyIf(
this.codeMirror || {}, {
lineNumbers: true,
theme: 'cobalt',
mode: mode,
tabSize: 4,
indentUnit: 4,
onCursorActivity: function() {
me.codeMirrorEditor.matchHighlight("CodeMirror-matchhighlight");

me.codeMirrorEditor.setLineClass(hlLine, null, null);
hlLine = me.codeMirrorEditor.setLineClass(me.codeMirrorEditor.getCursor().line, null, "xcme-activeline");
},
onChange: function(){

var sCode = me.codeMirrorEditor.getValue();
oCmp.setValue(sCode);

if(me.oldSourceCode == sCode){
me.setTitleClass(true);
}else{
me.setTitleClass();
}
},
onGutterClick: foldFunc,
extraKeys: {
"Ctrl-Q": function(cm){
foldFunc(cm, cm.getCursor().line);
},
"Ctrl-S": function(cm){
me.triggerOnSave.defer(1, me);
}
}
}
);


this.codeMirrorEditor = CodeMirror.fromTextArea(document.getElementById(oCmp.id), editorConfig);
hlLine = me.codeMirrorEditor.setLineClass(0, "xcme-activeline");

// Disable spell check button for non-js content
var sParserType = oThis.parser || 'defo';
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('easyCodeMirror', Easy.CodeMirror);





Please note that this component extends Panel using "Easy" namespace. Replace that with whatever forks for you(eg: Ext.ux.?). Also note that it is tailored to run as a tab panel (see setTitleClass method), so you can also remove that if oyu don't need it.

Jangla
6 Apr 2013, 11:31 AM
Hi,

I have made a port to ExjJS 4.0 and CodeMirror 2.0, hope someone can use it.

26301

Having some issues with this in Ext 4 when used on a tab panel. Getting this error:
Uncaught TypeError: Cannot read property 'measuresBox' of null


EDIT: Looks like this is due to me trying to define a height for the control. Also have another issue - I can't hide the toolbar for some reason.