PHP Code:
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):
PHP Code:
/*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.