matefun.component.ts 21.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Component, NgModule, ViewChild, HostListener, ElementRef, ComponentRef, TemplateRef } from '@angular/core';
import { CanvasModule} from '../canvas/canvas.module';
import { CanvasComponent } from '../canvas/canvas.component';
import { Http, JsonpModule } from '@angular/http';
import { Headers, RequestOptions } from '@angular/http';
import { HaskellService } from '../../shared/services/haskell.service';
import { WebsocketService } from '../../shared/services/websocket.service';
import { UsuarioService } from '../../shared/services/usuario.service';
import { SessionService } from '../../shared/services/session.service';
import { GHCIService } from '../../shared/services/ghci.service';
import { AuthenticationService } from '../../shared/services/authentication.service';
import { GHCI_URL } from '../../shared/config';
import { Archivo } from '../../shared/objects/archivo';
import { Configuracion } from '../../shared/objects/usuario';
import { ConfirmComponent } from './confirm.component';
import { SeleccionarDirectorioComp } from './seleccionarDirectorio.component';
import { DialogService } from "ng2-bootstrap-modal";
import { CodemirrorComponent } from 'ng2-codemirror';
import { NgbPopoverConfig, NgbPopover} from '@ng-bootstrap/ng-bootstrap';
import { NgbPopoverWindow } from '@ng-bootstrap/ng-bootstrap/popover/popover';
import { NotificacionService } from '../../shared/services/notificacion.service';

import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
25
// import 'codemirror/mode/haskell/haskell';
26
27
28
29
30
31
32
33
34
35
36
37
import 'codemirror/addon/display/panel';
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/hint/anyword-hint';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/lib/codemirror';
import 'codemirror/addon/search/search';


import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/search/search';
import 'codemirror/addon/search/matchesonscrollbar';
import 'codemirror/addon/search/jump-to-line';
38
import 'codemirror/addon/edit/matchbrackets';
39

40
41
import './codemirror-matefun-mode.js'

42
43
44
45
46
47
48
49
var codeMirrorRef:any;
var componentRef : any;
var focus: any;
@Component({
    moduleId: module.id,
    selector: 'matefun',     
    templateUrl: './matefun.component.html',   
    styleUrls: ['./matefun.component.scss'],  
50
    providers: [ WebsocketService, NgbPopoverConfig, UsuarioService ]
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
})


export class MateFunComponent {

    consoleDisable: boolean = false;
    consolaVisible: boolean = true;
    cursorPanel: any;
    cursorPanelLabel: any;
    cursorLabelInit : boolean = false;
    entrada : string = '';
    archivo : Archivo;
    copiaNombreArchivo:string;
    copiaContenidoArchivo:string;
    modificado = false;
    argumentoI = false;
    argumentoF = false;
    editableLoaded = false;
    editDialogFired = false;
    archivosTree :any;
    idRecorridos: any;
    code: string ='';
    configCodeMirror = {
        readOnly: false,
        lineNumbers: true,
        lineWrapping : true,
77
        matchBrackets: true,
78
79
        extraKeys: {"Ctrl-Space": "autocomplete"},
        mode: {
80
            name: "matefun", 
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
            globalVars: true
        },
        gutters: ["CodeMirror-linenumbers", "breakpoints"],
        theme: 'dracula',
        fontSize: 12
    };
    themes = ['3024-day', '3024-night', 'abcdef', 'ambiance-mobile', 'ambiance', 'base16-dark', 'base16-light', 'bespin', 'blackboard', 'cobalt', 'colorforth', 'dracula', 'duotone-dark', 'duotone-light', 'eclipse', 'elegant', 'erlang-dark', 'hopscotch', 'icecoder', 'isotope', 'lesser-dark', 'liquibyte', 'material', 'mbo', 'mdn-like', 'midnight', 'monokai', 'neat', 'neo', 'night', 'panda-syntax', 'paraiso-dark', 'paraiso-light', 'pastel-on-dark', 'railscasts', 'rubyblue', 'seti', 'solarized', 'the-matrix', 'tomorrow-night-bright', 'tomorrow-night-eighties', 'ttcn', 'twilight', 'vibrant-ink', 'xq-dark', 'xq-light', 'yeti', 'zenburn']


    constructor(
        private haskellService: HaskellService,
        private authService: AuthenticationService, 
        private ghciService: GHCIService, 
        private elRef: ElementRef, 
        private notifService: NotificacionService,
        private sessionService: SessionService,
        private dialogService:DialogService,
        private usuarioService: UsuarioService) {
        //si el archivo fue seteado en la session. 
        this.archivo = sessionService.getArchivo();
        if(!this.archivo || !this.archivo.id){
            this.newFile();
        }
        this.copiaContenidoArchivo = this.archivo.contenido;
        this.copiaNombreArchivo = this.archivo.nombre;
        if(authService.getUser().configuracion){
            var config: Configuracion = authService.getUser().configuracion;
            if(config.fontSizeEditor<=30 && config.fontSizeEditor>=8){
                this.configCodeMirror.fontSize = config.fontSizeEditor;
            }
            if(this.themes.some(theme => theme==config.themeEditor)){
                this.configCodeMirror.theme = config.themeEditor;                
            }
            sessionStorage.setItem('codeMirrorConfig',JSON.stringify(this.configCodeMirror));
            this.argumentoI = config.argumentoI;
            this.argumentoF = config.argumentoF;

        }
        this.code = "my code";
        let svg : string = '';

    }


    @ViewChild(CodemirrorComponent) codemirror: CodemirrorComponent;
    // @ViewChild(NgbPopover) popover: NgbPopover;
    @ViewChild('popover') popover: NgbPopover;

    updateConfig(theme){
        this.configCodeMirror.theme = theme;
        this.codemirror.instance.setOption('theme', theme); 
        sessionStorage.setItem('codeMirrorConfig',JSON.stringify(this.configCodeMirror));
    }

    lockSaveButton (){
        this.copiaNombreArchivo = this.archivo.nombre;
        this.copiaContenidoArchivo = this.archivo.contenido;
        this.modificado = false;
    }
    

    showConfirm() {
        let disposable = this.dialogService.addDialog(ConfirmComponent, {
            title:'Está intentando editar un archivo de solo lectura', 
            message:'Está editando un archivo de solo lectura, desea continuar?'})
        .subscribe((isConfirmed)=>{

            if(isConfirmed) {
                codeMirrorRef.options.readOnly = false;
                componentRef.editDialogFired = true;
            }
152
153
        });        
    }
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
    /* Panel para la posición del cursor */
    makePanel() {

        var node = document.createElement("div");
        node.id = "cursorpos-panel";
        node.className = "panel bottom";
        this.cursorPanelLabel = node.appendChild(document.createElement("span"));
        var cm = this.codemirror.instance;
        var x = cm.getCursor().line;
        var y = cm.getCursor().ch; 
        x = (Number(x) + 1).toString();
        y = (Number(y) + 1).toString();
        this.cursorPanelLabel.textContent = "Posición del cursor: (" + x + "," + y + ")";    


        this.cursorPanel = this.codemirror.instance.addPanel(node, {position: "bottom", stable: true});
        var that = this;
        //agregamos el evento que setea la posición
        this.codemirror.instance.on("cursorActivity",function(cm){
174
175
176
177
            var x = cm.getCursor().line;
            var y = cm.getCursor().ch; 
            x = (Number(x) + 1).toString();
            y = (Number(y) + 1).toString();
178
179
            that.cursorPanel.node.innerText = "Posición del cursor: (" + x + "," + y + ")";    
        });
180

181
182
183
184
185
186
187
        this.codemirror.instance.on("keyHandled",function(cm,name,evt){
            if(name.code==="Digit1" && name.ctrlKey && name.shiftKey){
                that.seleccionarDirectorio();
            } else if(name.code==="Digit2" && name.ctrlKey && name.shiftKey){
                that.saveConfig();
            }
        });
188

189
        this.codemirror.instance.on("keypress",function(cm,name,evt){
190

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
            if(!that.editDialogFired && JSON.parse(sessionStorage.currentUser).tipo === "docente" && cm.options.readOnly){
                codeMirrorRef = that.codemirror.instance;
                componentRef = that;
                that.showConfirm(); 
            }

        });
    } 

    saveConfig(){
        var config = new Configuracion();
        config.themeEditor = this.configCodeMirror.theme;
        config.fontSizeEditor = this.configCodeMirror.fontSize;
        var confUser = this.authService.getUserConfig();
        var reiniciar = confUser.argumentoF != this.argumentoF || confUser.argumentoI != this.argumentoI;
        config.argumentoF = this.argumentoF;
        config.argumentoI = this.argumentoI;
        this.usuarioService.actualizarConfiguracion(this.authService.getUser().cedula,config)
        .subscribe(
            success=> {
                //this.ghciService.consoleRef.Write("Configuración guardada"  + "\n");
                this.popover.close();
                this.authService.setUserConfig(success);
                if(reiniciar){
                    this.reiniciarInterprete();
216
217
                }

218
219
220
221
            },
            error=> {
                this.notifService.error(error);
                this.popover.close();
222
            }
223
224
225
226
227
228
            );
    }

    aumentarFuente(){
        if(this.configCodeMirror.fontSize<30){
            this.configCodeMirror.fontSize++;
229
        }
230
    }
231

232
233
234
    disminuirFuente(){
        if(this.configCodeMirror.fontSize>8){
            this.configCodeMirror.fontSize--;
235
        }
236
    }
237

238
239
    @HostListener('document:click', ['$event'])
    private documentClicked(event: MouseEvent): void {
240

241
242
        // Popover is open
        if (this.popover && this.popover.isOpen()) {
243

244
245
            // Not clicked on self element
            if (!(this.popover as any)._elementRef.nativeElement.contains(event.target)) {
246

247
248
                // Hacking typescript to access private member
                const popoverWindowRef: ComponentRef<NgbPopoverWindow> = (this.popover as any)._windowRef;
249

250
251
252
                // If clicked outside popover window
                if (!popoverWindowRef.location.nativeElement.contains(event.target)) {
                    this.popover.close();
253
254
255
                }
            }
        }
256
    }
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
    ngOnInit() {

        this.ghciService.rendered(); 


        this.haskellService.getArchivos(this.authService.getUser().cedula)
        .subscribe(
            archivos => {
                //.filter(function(a){return !a.eliminado})
                this.buildTreeFromList(archivos);

            }, 
            error => console.log("Error al obtener los archivos del alumno") 
            );

        function KeyPress(e) {

            var evtobj = window.event? event : e
            if (evtobj.keyCode == 90 && evtobj.ctrlKey){
                //alert("Ctrl+z")
            };
            if(evtobj.key.toLowerCase() ==="a" && evtobj.ctrlKey){
                componentRef.seleccionarDirectorio();
                return false;
            }else if(evtobj.key.toLowerCase() ==="e" && evtobj.ctrlKey){
                componentRef.downloadFile();
                return false;
            } else if(evtobj.key.toLowerCase() ==="r" && evtobj.ctrlKey){
                componentRef.reiniciarInterprete();
                return false;
            } else if(evtobj.key.toLowerCase() ==="g" && evtobj.ctrlKey){
                componentRef.guardarArchivo();
                return false;
            } else if(evtobj.key.toLowerCase() ==="o" && evtobj.ctrlKey){
                document.getElementById("popover").click();
                return false;
            } else if(evtobj.ctrlKey && evtobj.altKey && evtobj.key.toLowerCase() ==="p"){
                document.getElementById("ProgramBtn").click();
                var that  = componentRef;
                setTimeout(function() {
                    that.codemirror.instance.focus();
                },250);
                componentRef.codemirror.instance.focus();
                focus ="program";
                return false;
            }  else if(evtobj.ctrlKey && evtobj.altKey && evtobj.key.toLowerCase() ==="c"){
                componentRef.ghciService.focusConsole();
                focus = "consola";
                return false;
            }  else if(evtobj.ctrlKey && evtobj.altKey && evtobj.key.toLowerCase() ==="f"){
                document.getElementById("FigurasBtn").click()
                componentRef.ghciService.focusConsole();
                focus = "graficas";
                return false;
            }  else if(evtobj.key.toLowerCase() ==="p" && evtobj.ctrlKey && !evtobj.altKey){
                componentRef.runCode();
                return false;
            } 
316
        }
317
318
        document.onkeydown = KeyPress;
    }
319

320
321
322
323
324
325
326
327
328
329
330
331
    ngAfterViewInit() {
        componentRef = this;
        if(this.codemirror.instance!=null && !this.cursorLabelInit){
            this.cursorLabelInit = true;
            this.codemirror.instance.setOption('theme', this.configCodeMirror.theme);
            this.makePanel();    
        }
        if(!this.editableLoaded && this.codemirror.instance!=null &&(this.sessionService.archivo.editable!==undefined)){
            try{
                var editable = this.sessionService.archivo.editable && (this.sessionService.archivo.estado == 'Edicion' || this.sessionService.archivo.estado == 'Devuelto');
                this.codemirror.instance.options.readOnly = !editable;
                this.editableLoaded = true;
332
333


334
335
336
337
            } catch(err) {
                return;

            }
338
339
        }

340
341
342
343
344
345
346
347
348
349
350
351
    }
    
    htmlEncode(value:string){
        return value
        .replace('Prelude> ','')
        .replace(/&/g, '&amp;')
        .replace(/\s/g, '&nbsp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
    }
352

353
    @ViewChild(CanvasComponent) canvasC: CanvasComponent;
354

355
356
357
358
    funcionSTR: string = 'Math.sin(x)*x*x-20';
    consola: string = '';
    command: string = '';
    tipo: number = 1;
359

360
361
362
363
    private onKey = function (value: string) {
        this.funcionSTR = value;
        this.archivo.contenido = value;
    }
364

365
366
367
    private writeCommand = function (value: string){
        this.command = value.split("\n")[value.split("\n").length - 1];
    }
368

369
370
371
372
    private selectFunction = function() {
        this.tipo = 1;
        this.funcionSTR = "Math.sin(x)*x*x-20";
    }
373

374
375
376
377
    private selectElipse = function() {
        this.tipo = 2;
        this.funcionSTR = "elipse(x,y,radioX, radioY, rotacion_en_grados)";
    }
378

379
380
381
382
    private selectCircle = function() {
        this.tipo = 3;
        this.funcionSTR = "circulo(x,y,radio)";
    }
383

384
385
386
    private elipse = function(x: number, y: number, radiusX: number, radiusY: number, rotation: number) {
        return [x, y, radiusX, radiusY, rotation];
    }
387

388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
    private circulo = function(x: number, y: number, radius: number) {
        return [x, y, radius];
    }

    inputConsola(text:any){
        this.entrada = text;            
    }
    newFile(){
        this.archivo = new Archivo();
        this.archivo.cedulaCreador = this.authService.getUser().cedula;
        this.archivo.contenido = "";
        this.archivo.nombre = "";
        this.copiaNombreArchivo = '';
        this.copiaContenidoArchivo = '';
    }

    archivoModificado(){
        if(this.copiaNombreArchivo!=this.archivo.nombre || this.copiaContenidoArchivo != this.archivo.contenido){
            this.modificado = true;
        }else{
            this.modificado = false;
409
        }
410
    }
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
    guardarArchivo(){
        var regex = /^[A-Z]/
        if(this.archivo.nombre.trim() == ""){
            this.notifService.error("Nombre de archivo sin especificar");
        }else if (!regex.test(this.archivo.nombre)){
            this.notifService.error("Nombre de archivo debe iniciar con mayusula.")
        }else{
            if(this.archivo.id){
                this.haskellService.editarArchivo(this.archivo.id, this.archivo)
                .subscribe(
                    archivo => {
                        //this.ghciService.consoleRef.Write("Editar archivo: " + this.archivo.nombre + "\n");
                        this.archivo = archivo;
                        this.lockSaveButton();
                    }, 
                    error => {
                        this.notifService.error(error);
                    });
430
            }else{
431
432
433
434
435
436
437
438
439
440
441
                this.haskellService.crearArchivo(this.archivo)
                .subscribe(
                    archivo => {
                        //this.ghciService.consoleRef.Write("Archivo creado: " + this.archivo.nombre + "\n");
                        this.archivo = archivo;
                        this.lockSaveButton();
                    }, 
                    error => {
                        this.notifService.error(error);
                    });

442
443
            }
        }
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
    }
    runCode(){

        this.ghciService.setCodemirrorRef(this.codemirror.instance);
        this.ghciService.resetGutters();
        var regex = /^[A-Z]/
        if(this.archivo.nombre.trim() == ""){
            this.notifService.error("Nombre de archivo sin especificar");
        }else if (!regex.test(this.archivo.nombre)){
            this.notifService.error("Nombre de archivo debe iniciar con mayusula.")
        }else{

            var resultado = this.sessionService.cargarDependencias(this.archivo);
            if(resultado["status"]==="miss"){
                this.ghciService.outputConsole("Error: No se encuentra el archivo " + resultado["nombre"] + "\n");
                return;
            }
            if(this.archivo.id){
                if(this.archivo.editable || this.authService.getUser().tipo == 'docente'){
463
464
465
466
                    this.haskellService.editarArchivo(this.archivo.id, this.archivo)
                    .subscribe(
                        archivo => {
                            this.archivo = archivo;
467
468
469
470
471
472
473
474
                            var list = this.sessionService.getDependencias(),
                            idList = [];
                            for(var l in list){
                                idList.push(list[l].id);
                            }
                            if(!idList.some(id => id ==archivo.id)){
                                idList.push(archivo.id);
                            }
475
                            this.lockSaveButton();
476
                            this.ghciService.loadFile(archivo.id,idList);
477
478
479
480
481
                        }, 
                        error => {
                            this.notifService.error(error);
                        });
                }else{
482
483
484
485
                    var list = this.sessionService.getDependencias(),
                    idList = [];
                    for(var l in list){
                        idList.push(list[l].id);
486
                    }
487
488
489
490
                    if(!idList.some(id => id ==this.archivo.id)){
                        idList.push(this.archivo.id);
                    }
                    this.ghciService.loadFile(this.archivo.id,idList);
491
                }
492
493
494
495
496
497
498
499
500
501
502
            }else{
                this.haskellService.crearArchivo(this.archivo)
                .subscribe(
                    archivo => {
                        this.archivo = archivo;
                        this.lockSaveButton();
                        this.ghciService.loadFile(archivo.id,[]);
                    }, 
                    error => {
                        this.notifService.error(error);
                    });
503
504
            }
        }
505
        this.ghciService.focusConsole();
506

507
508
509
510
511
    }
    download(filename, text) {
        var element = document.createElement('a');
        element.setAttribute('href', 'data:application/octet-stream,' + encodeURIComponent(text));
        element.setAttribute('download', filename+ ".mf");
512

513
514
        element.style.display = 'none';
        document.body.appendChild(element);
515

516
        element.click();
517

518
519
520
521
522
523
524
        document.body.removeChild(element);
    }
    downloadFile(){
        var nom = this.archivo.nombre;
        var content = this.archivo.contenido;
        if(nom!= undefined && nom!="" && content!= undefined && content !=""){
            this.download(nom , content);
525
526
        }

527
528
529
530
    }
    reiniciarInterprete(){
        this.ghciService.reiniciarInterprete();
    }
531

532
533
534
    toggleConsole(){
        this.consolaVisible = !this.consolaVisible;
    }
535

536
537
538
539
540
541
542
543
544
545
546
    seleccionarDirectorio(){
        this.archivosTree = this.sessionService.getArchivos(undefined);
        var that = this;
        let disposable = this.dialogService.addDialog(SeleccionarDirectorioComp, {
            title:'', 
            message:'',
            archivos:this.archivosTree,
            directorioActual:this.archivosTree,
            nombre:'',
            parent:this})
        .subscribe((isConfirmed)=>{
547

548
            if(isConfirmed) {
549
550


551
552
553
554
555
                //codeMirrorRef.options.readOnly = false;
                //componentRef.editDialogFired = true;
            }
        });
    }
556
557


558
    buildTreeFromList (archivos){
559
560


561
562
563
564
565
566
567
568
        this.sessionService.setArchivosList(archivos);
        var root :Archivo;

        for(var a in archivos){
            var arch = archivos[a];
            if(arch.padreId===-1){
                root = arch;
            } 
569
        }
570
571
572
573
574
575
576
577
578
579
        this.idRecorridos = [root.id];
        var archivos2 = archivos.filter(
            function(a){
                return a.id!==root.id;
            }
            );
        var tree = this.buildTree(archivos2,root);
        this.archivosTree = tree;
        this.sessionService.setArchivosTree(tree);
    }
580
581


582
583
584
585
586
587
588
    buildTree(archivos, root){
        root.archivos = this.getArchivos(root.id,archivos);
        for(var a in root.archivos){
            if(root.archivos[a].directorio && this.idRecorridos[root.archivos[a].id] === undefined){
                var id = root.archivos[a].id;
                var archivos2 = archivos.filter(function(a){return a.id!==id});
                root.archivos[a] = this.buildTree(archivos2 ,root.archivos[a]);
589
590
            }
        }
591
592
        return root;
    }
593

594
595
596
597
598
599
    getArchivos(id,archivos){
        return archivos.filter(
            function(a){
                return a.padreId === id;
            });
    }
600
601
602



603
}