7b478767b878286839a298b93a5c494553536ccf
[Packages/TYPO3.CMS.git] / typo3 / sysext / em / res / js / em_files.js
1 /**
2 * ExtJS for the extension manager.
3 *
4 *
5 * @author Steffen Kamper <info@sk-typo3.de>
6 * @package TYPO3
7 * @subpackage extension manager
8 * @version $Id: $
9 */
10
11 Ext.ns('TYPO3.EM');
12
13 TYPO3.EM.ExtFilelist = Ext.extend(Ext.Panel, {
14 recordData: null,
15 isWindow: false,
16 loaderUrl: null,
17 rootIcon: 'sysext/t3skin/icons/module_tools_em.png',
18 rootText: TYPO3.lang.ext_details_ext_files,
19 baseParams: null,
20 treeId: null,
21
22 initComponent:function() {
23
24 var hlEditor = new TYPO3.EM.CodeMirror({
25 parser: 'mixed',
26 itemId: 'hlEditor',
27 stylesheet: TYPO3.settings.EM.editorCss,
28 editFile: null
29 });
30
31
32
33
34 var fileTree = new Ext.tree.TreePanel ({
35 itemId: 'extfiletree',
36 cls: 'extfiletree',
37 margins: '0 0 0 0',
38 cmargins: '0 0 0 0',
39 id: this.treeId ? this.treeId : Ext.id(),
40 stateful: this.treeId ? true : false,
41 stateEvents: [],
42 plugins: new Ext.ux.state.TreePanel(),
43
44 root: {
45 text: this.rootText,
46 itemId: 'fileroot',
47 expanded: true,
48 icon: this.rootIcon
49 },
50 loader: {
51 directFn: this.loaderUrl || TYPO3.EM.ExtDirect.getExtFileTree,
52 baseParams: this.baseParams ? this.baseParams : {
53 extkey: this.recordData.extkey,
54 typeShort: this.recordData.typeShort,
55 baseNode: this.recordData.nodePath
56 },
57 paramsAsHash: true
58 },
59 listeners: {
60 click: function(node) {
61 if (node.attributes.fileType === 'text') {
62 this.layout.center.panel.reloadButton.show().disable();
63 TYPO3.EM.ExtDirect.readExtFile(node.attributes.id , function(response) {
64 // load in textarea
65 hlEditor.openText(response, node.attributes.ext);
66 hlEditor.editFile = node.attributes.id;
67 this.layout.center.panel.reloadButton.enable();
68 this.layout.center.panel.fileLabel.setText('File: ' + hlEditor.editFile);
69 this.layout.center.panel.fileLabel.removeClass('fileChanged');
70 this.layout.center.panel.saveButton.disable();
71 this.layout.center.panel.undoButton.enable();
72 this.layout.center.panel.redoButton.enable();
73 this.layout.center.panel.indentButton.enable();
74 if (node.attributes.ext == 'js') {
75 this.layout.center.panel.jslintButton.enable();
76 } else {
77 this.layout.center.panel.jslintButton.disable();
78 }
79
80 }, this);
81 }
82 if (node.attributes.fileType === 'image') {
83 var w = new Ext.Window({
84 width: 200,
85 height: 200,
86 title: node.attributes.text,
87 layout: 'fit',
88 items: [{
89 xtype: 'image',
90 src: TYPO3.settings.EM.siteUrl + node.attributes.id,
91 autoSize: true,
92 resizable: false,
93 renderTo: document.body
94 }]
95 }).show();
96 }
97 if (node.isLeaf()) {
98 this.layout.west.panel.downloadFileButton.enable();
99 } else {
100 this.layout.west.panel.downloadFileButton.disable();
101 }
102 },
103 scope: this
104 }
105
106 });
107
108 Ext.apply(this, {
109
110 layout: 'border',
111 items: [{
112 region: 'west',
113 layout: 'fit',
114 split: true,
115 width: 260,
116 collapsible: true,
117 collapseMode: 'mini',
118 cls: 'filetree-panel',
119 hideCollapseTool: true,
120 items: [fileTree],
121 tbar: [{
122 iconCls: 'x-tbar-loading',
123 handler: function() {
124 fileTree.getRootNode().reload();
125 },
126 scope: this
127 }, {
128 iconCls: 'x-btn-upload',
129 tooltip: TYPO3.lang.cmd_upload,
130 ref: '../uploadFileButton',
131 hidden: true
132 }, {
133 iconCls: 't3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-extension-download',
134 tooltip: TYPO3.lang.cmd_download,
135 ref: '../downloadFileButton',
136 disabled: true,
137 handler: function() {
138 var node = fileTree.getSelectionModel().getSelectedNode();
139 if (node.isLeaf()) {
140 this.downloadFile(node.attributes.id);
141 }
142 },
143 scope: this
144 }]
145 }, {
146
147 region: 'center',
148
149 layout: 'fit',
150 margins: '0 0 0 0',
151 cmargins: '0 0 0 0',
152 border: false,
153 cls: 'file-editor',
154 items: [hlEditor],
155 tbar: [{
156 iconCls: 'x-btn-filebrowser',
157 tooltip: TYPO3.lang.cmd_openInNewWindow,
158 ref: '../openWindowButton',
159 scope: this,
160 hidden: this.isWindow || this.noWindowOpen,
161 handler: function() {
162
163 var newEditor = new Ext.Window({
164 title: this.recordData.title + ' (' + this.recordData.extkey + ')',
165 width: 600,
166 height: 400,
167 layout: 'fit',
168 maximizable: true,
169 collapsible: true,
170 items: [{
171 xtype: 'extfilelist',
172 minHeight: 400,
173 recordData: this.recordData,
174 isWindow: true
175 }]
176 }).show();
177 }
178 }, {
179 xtype: 'tbseparator',
180 hidden: this.isWindow
181 }, {
182 iconCls: 'x-tbar-loading',
183 tooltip: TYPO3.lang.cmd_reloadFile,
184 ref: '../reloadButton',
185 scope: this,
186 hidden: true,
187 handler: function() {
188 if (hlEditor.editFile) {
189 this.layout.center.panel.reloadButton.disable();
190 TYPO3.EM.ExtDirect.readExtFile(hlEditor.editFile , function(response) {
191 hlEditor.setValue(response);
192 this.layout.center.panel.reloadButton.enable();
193 }, this);
194 }
195 }
196 }, {
197 iconCls: 'x-btn-save',
198 tooltip: TYPO3.settings.EM.fileSaveAllowed ? TYPO3.lang.cmd_save : TYPO3.lang.ext_details_saving_disabled,
199 ref: '../saveButton',
200 disabled: true,
201 scope: this,
202 handler: function() {
203 this.layout.center.panel.reloadButton.disable();
204 var file = this.layout.west.items[0].getSelectionModel().getSelectedNode().attributes.id;
205 TYPO3.EM.ExtDirect.saveExtFile(
206 file,
207 hlEditor.getValue(),
208 function(response) {
209 if (response.success) {
210 TYPO3.Flashmessage.display(TYPO3.Severity.ok, TYPO3.lang.cmd_save, String.format(TYPO3.lang.msg_fileSaved, response.file), 5);
211 this.layout.center.panel.saveButton.disable();
212 this.layout.center.panel.reloadButton.enable();
213 this.layout.center.panel.fileLabel.removeClass('fileChanged');
214 } else {
215 TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.cmd_save, response.error, 5);
216 }
217 }, this);
218 }
219 },
220 {
221 iconCls: 'x-btn-undo',
222 tooltip: TYPO3.lang.cmd_undo,
223 ref: '../undoButton',
224 disabled: true,
225 scope: this,
226 handler: function() {
227 hlEditor.codeMirrorEditor.undo();
228 }
229 },
230 {
231 iconCls: 'x-btn-redo',
232 tooltip: TYPO3.lang.cmd_redo,
233 ref: '../redoButton',
234 disabled: true,
235 scope: this,
236 handler: function() {
237 hlEditor.codeMirrorEditor.redo();
238 }
239 },
240 {
241 iconCls: 'x-btn-indent',
242 tooltip: TYPO3.lang.cmd_indent,
243 ref: '../indentButton',
244 disabled: true,
245 scope: this,
246 handler: function() {
247 hlEditor.codeMirrorEditor.reindent();
248 }
249 },
250 {
251 iconCls: 'x-btn-jslint',
252 tooltip: TYPO3.lang.cmd_jslint,
253 ref: '../jslintButton',
254 disabled: true,
255 scope: this,
256 handler: function() {
257 try {
258 var bValidates = JSLINT(this.findByType('textarea')[0].getValue());
259
260 var oStore = hlEditor.debugWindow.findByType('grid')[0].getStore();
261 if (!bValidates) {
262 var aErrorData = [];
263
264 for (var err in JSLINT.errors) {
265 if (JSLINT.errors.hasOwnProperty(err) && (JSLINT.errors[err] !== null)) {
266 aErrorData.push([JSLINT.errors[err].line, JSLINT.errors[err].character, JSLINT.errors[err].reason]);
267 }
268 }
269
270 oStore.loadData(aErrorData, false);
271 hlEditor.debugWindow.show();
272
273 }
274 else {
275
276 oStore.loadData([
277 [1, 1, TYPO3.lang.msg_congratsNoErrors]
278 ], false);
279 hlEditor.debugWindow.show();
280 }
281 } catch(e) {
282 }
283 }
284 },
285 '->',
286 {
287 xtype: 'tbtext',
288 ref: '../fileLabel',
289 itemId: 'editarea-filename',
290 text: TYPO3.lang.help_loadFileInEditor
291 }]
292 }]
293 });
294
295 TYPO3.EM.ExtFilelist.superclass.initComponent.apply(this, arguments);
296
297 },
298
299 onRender: function() {
300 TYPO3.EM.ExtFilelist.superclass.onRender.apply(this, arguments);
301 },
302
303 downloadFile: function(path) {
304
305 // create hidden target iframe
306 var id = Ext.id();
307 var frame = document.createElement('iframe');
308 frame.id = id;
309 frame.name = id;
310 frame.className = 'x-hidden';
311 if (Ext.isIE) {
312 frame.src = Ext.SSL_SECURE_URL;
313 }
314
315 document.body.appendChild(frame);
316
317 if (Ext.isIE) {
318 document.frames[id].name = id;
319 }
320
321 var form = Ext.DomHelper.append(document.body, {
322 tag: 'form',
323 method: 'post',
324 action: 'mod.php?M=tools_em',
325 target: id
326 });
327
328 document.body.appendChild(form);
329
330 var hidden;
331
332 // append path to form
333 hidden = document.createElement('input');
334 hidden.type = 'hidden';
335 hidden.name = 'CMD[downloadExtFile]';
336 hidden.value = path;
337 form.appendChild(hidden);
338
339 var callback = function() {
340 Ext.EventManager.removeListener(frame, 'load', callback, this);
341 setTimeout(function() {
342 document.body.removeChild(form);
343 }, 100);
344 setTimeout(function() {
345 document.body.removeChild(frame);
346 }, 110);
347 };
348
349 Ext.EventManager.on(frame, 'load', callback, this);
350
351 form.submit();
352 }
353
354
355
356 });
357
358 // register xtype
359 Ext.reg('extfilelist', TYPO3.EM.ExtFilelist);
360
361
362 TYPO3.EM.CodeMirrorConfig = {
363 cssPath: TYPO3.settings.EM.codemirrorCssPath,
364 jsPath: TYPO3.settings.EM.codemirrorJsPath,
365 parser: {
366 defo: { // js code
367 parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
368 stylesheet: [TYPO3.settings.EM.codemirrorCssPath + "jscolors.css"]
369 },
370 css: {
371 parserfile: ["parsecss.js"],
372 stylesheet: [TYPO3.settings.EM.codemirrorCssPath + "csscolors.css"]
373 },
374 js: {
375 parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
376 stylesheet: [TYPO3.settings.EM.codemirrorCssPath + "jscolors.css"]
377 },
378 php: {
379 parserfile: ["../contrib/php/js/tokenizephp.js", "../contrib/php/js/parsephp.js"],
380 stylesheet: [TYPO3.settings.EM.codemirrorContribPath + "php/css/phpcolors.css"]
381 },
382 html: {
383 parserfile: ["parsexml.js", "parsecss.js", "tokenizejavascript.js", "parsejavascript.js", "../contrib/php/js/tokenizephp.js", "../contrib/php/js/parsephp.js", "../contrib/php/js/parsephphtmlmixed.js"],
384 stylesheet: [
385 TYPO3.settings.EM.codemirrorCssPath + "xmlcolors.css",
386 TYPO3.settings.EM.codemirrorCssPath + "jscolors.css",
387 TYPO3.settings.EM.codemirrorCssPath + "csscolors.css",
388 TYPO3.settings.EM.codemirrorContribPath + "php/css/phpcolors.css"]
389
390 },
391 mixed: {
392 parserfile: ["parsexml.js", "parsecss.js", "tokenizejavascript.js", "parsejavascript.js", "../contrib/php/js/tokenizephp.js", "../contrib/php/js/parsephp.js", "../contrib/php/js/parsephphtmlmixed.js"],
393 stylesheet: [
394 TYPO3.settings.EM.codemirrorCssPath + "xmlcolors.css",
395 TYPO3.settings.EM.codemirrorCssPath + "jscolors.css",
396 TYPO3.settings.EM.codemirrorCssPath + "csscolors.css",
397 TYPO3.settings.EM.codemirrorContribPath + "php/css/phpcolors.css"
398 ]
399 }
400 }
401 };
402
403
404 TYPO3.EM.CodeMirror = Ext.extend(Ext.Panel, {
405 layout: 'fit',
406 sourceCode: '',
407 stylesheet: null,
408 initComponent: function() {
409 // add custom stylesheet to all parser
410 if (this.stylesheet) {
411 Ext.iterate(TYPO3.EM.CodeMirrorConfig.parser, function(key, value) {
412 value.stylesheet.push(this.stylesheet);
413 }, this);
414 }
415
416 this.contentChanged = false;
417 var me = this;
418 this.debugWindow = new Ext.Window({
419 title: TYPO3.lang.msg_debug,
420 width: 500,
421 layout: 'border',
422 closeAction: 'hide',
423 height: 160,
424 items: [new Ext.grid.GridPanel({
425 layout: 'fit',
426 region: 'center',
427 border: false,
428 listeners: {
429 rowclick: function(grid) {
430 var oData = grid.getSelectionModel().getSelected().data;
431 me.codeMirrorEditor.jumpToLine(oData.line);
432 }
433 },
434 store: new Ext.data.ArrayStore({
435 fields: [
436 {name: 'line'},
437 {name: 'character'},
438 {name: 'reason'}
439 ]
440 }),
441 columns: [
442 {
443 id: 'line',
444 header: TYPO3.lang.msg_line,
445 width: 60,
446 sortable: true,
447 dataIndex: 'line'
448 },
449 {
450 id: 'character',
451 header: TYPO3.lang.msg_character,
452 width: 60,
453 sortable: true,
454 dataIndex: 'character'
455 },
456 {
457 header: TYPO3.lang.show_description,
458 width: 240,
459 sortable: true,
460 dataIndex: 'reason'
461 }
462 ],
463 stripeRows: true
464 })]
465 });
466
467 Ext.apply(this, {
468 items: [
469 {
470 xtype: 'textarea',
471 readOnly: false,
472 hidden: true,
473 value: this.sourceCode
474 }
475 ]
476 });
477
478 TYPO3.EM.CodeMirror.superclass.initComponent.apply(this, arguments);
479 },
480
481 triggerOnSave: function() {
482 this.changeAction();
483 var sNewCode = this.codeMirrorEditor.getCode();
484 this.oldSourceCode = sNewCode;
485 this.onSave(arguments[0] || false);
486 },
487
488 onRender: function() {
489 this.oldSourceCode = this.sourceCode;
490 TYPO3.EM.CodeMirror.superclass.onRender.apply(this, arguments);
491 // trigger editor on afterlayout
492 this.on('afterlayout', this.triggerCodeEditor, this, {
493 single: true
494 });
495 this.on('resize', this.resizeCodeEditor, this);
496
497 },
498
499 /** @private */
500 resizeCodeEditor: function(component, width, height, origWidth, origHeight) {
501 var el = Ext.fly(this.codeMirrorEditor.frame);
502 el.setSize(width - 50, height); // subtract width of line numbers
503 el.next().setHeight(height);
504 this.doLayout();
505 },
506
507 /** @private */
508 triggerCodeEditor: function() {
509 var me = this;
510 var oCmp = this.findByType('textarea')[0];
511 this.editorConfig = Ext.applyIf(this.codeMirror || {}, {
512 lineNumbers: true,
513 textWrapping: false,
514 content: oCmp.getValue(),
515 indentUnit: 4,
516 tabMode: 'shift',
517 readOnly: oCmp.readOnly,
518 path: TYPO3.EM.CodeMirrorConfig.jsPath,
519 autoMatchParens: true,
520 initCallback: function(editor) {
521 try {
522 var iLineNmbr = ((Ext.state.Manager.get("edcmr_" + me.itemId + '_lnmbr') !== undefined) ? Ext.state.Manager.get("edcmr_" + me.itemId + '_lnmbr') : 1);
523 editor.jumpToLine(iLineNmbr);
524 } catch(e) {
525 }
526 },
527 onChange: function() {
528 var sCode = me.codeMirrorEditor.getCode();
529 oCmp.setValue(sCode);
530
531 if (me.oldSourceCode == sCode) {
532 me.changeAction(false);
533 } else {
534 me.changeAction(true);
535 }
536
537 }
538 });
539
540 var sParserType = me.parser || 'defo';
541 this.editorConfig = Ext.applyIf(this.editorConfig, TYPO3.EM.CodeMirrorConfig.parser[sParserType]);
542 this.codeMirrorEditor = new CodeMirror.fromTextArea(Ext.getDom(oCmp.id).id, this.editorConfig);
543
544 },
545
546 changeAction: function(changed) {
547 if (TYPO3.settings.EM.fileSaveAllowed) {
548 if (!changed) {
549 this.ownerCt.saveButton.disable();
550 this.ownerCt.fileLabel.removeClass('fileChanged');
551 this.contentChanged = false;
552 } else {
553 this.ownerCt.saveButton.enable();
554 this.ownerCt.fileLabel.addClass('fileChanged');
555 this.contentChanged = true;
556 }
557 }
558 },
559
560 getValue: function() {
561 return this.codeMirrorEditor.getCode();
562 },
563
564 setValue: function(text) {
565 this.codeMirrorEditor.setCode(text);
566 this.resizeCodeEditor();
567 },
568
569 setValueAtCursor: function(text) {
570 var cursorPosition = this.codeMirrorEditor.cursorPosition();
571 var handleForCursorLine = this.codeMirrorEditor.cursorLine();
572 this.codeMirrorEditor.insertIntoLine(handleForCursorLine, cursorPosition.character, text);
573 },
574
575 openText: function(text, parser) {
576 this.codeMirrorEditor.setCode(text);
577 }
578
579 });
580 Ext.reg('TYPO3.EM.CodeMirror', TYPO3.EM.CodeMirror);