Forked from
matefun / Frontend
147 commits behind the upstream repository.
-
Franco Pariani authoredFranco Pariani authored
matefun.component.ts 22.75 KiB
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 { Graph2DModule } from '../plotter/graph2D/graph2D.module';
import { Graph2DComponent } from '../plotter/graph2D/graph2D.component';
import { Graph3DComponent } from '../plotter/graph3D/graph3D.component';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
// import 'codemirror/mode/haskell/haskell';
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';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/comment/comment.js';
import './codemirror/matefun-mode-ES.js'
import './codemirror/matefun-mode-EN.js'
import * as npm from './../../../../package.json'
var codeMirrorRef:any;
var componentRef : any;
var focus: any;
@Component({
moduleId: module.id,
selector: 'matefun',
templateUrl: './matefun.component.html',
styleUrls: ['./matefun.component.scss'],
providers: [ WebsocketService, NgbPopoverConfig, UsuarioService ]
})
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,
matchBrackets: true,
styleActiveLine: true,
extraKeys: {"Ctrl-Space": "autocomplete"},
mode: {
name: "matefun-EN",
globalVars: true
},
gutters: ["CodeMirror-linenumbers", "breakpoints"],
theme: 'dracula',
fontSize: 12,
hintOptions: {
completeSingle: false
}
};
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']
version: string = npm.version;
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;
}
});
}
/* 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){
var x = cm.getCursor().line;
var y = cm.getCursor().ch;
x = (Number(x) + 1).toString();
y = (Number(y) + 1).toString();
that.cursorPanel.node.innerText = "Posición del cursor: (" + x + "," + y + ")";
});
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();
}
});
this.codemirror.instance.on("keypress",function(cm,name,evt){
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();
}
},
error=> {
this.notifService.error(error);
this.popover.close();
}
);
}
aumentarFuente(){
if(this.configCodeMirror.fontSize<30){
this.configCodeMirror.fontSize++;
}
}
disminuirFuente(){
if(this.configCodeMirror.fontSize>8){
this.configCodeMirror.fontSize--;
}
}
@HostListener('document:click', ['$event'])
private documentClicked(event: MouseEvent): void {
// Popover is open
if (this.popover && this.popover.isOpen()) {
// Not clicked on self element
if (!(this.popover as any)._elementRef.nativeElement.contains(event.target)) {
// Hacking typescript to access private member
const popoverWindowRef: ComponentRef<NgbPopoverWindow> = (this.popover as any)._windowRef;
// If clicked outside popover window
if (!popoverWindowRef.location.nativeElement.contains(event.target)) {
this.popover.close();
}
}
}
}
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;
}
}
document.onkeydown = KeyPress;
}
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;
} catch(err) {
return;
}
}
}
htmlEncode(value:string){
return value
.replace('Prelude> ','')
.replace(/&/g, '&')
.replace(/\s/g, ' ')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
}
// @ViewChild(CanvasComponent) canvasC: CanvasComponent;
@ViewChild(Graph3DComponent) graph3DComp: Graph3DComponent;
@ViewChild(Graph2DComponent) graph2DComp: Graph2DComponent;
funcionSTR: string = 'Math.sin(x)*x*x-20';
consola: string = '';
command: string = '';
tipo: number = 1;
private onKey = function (value: string) {
this.funcionSTR = value;
this.archivo.contenido = value;
}
private writeCommand = function (value: string){
this.command = value.split("\n")[value.split("\n").length - 1];
}
private selectFunction = function() {
this.tipo = 1;
this.funcionSTR = "Math.sin(x)*x*x-20";
}
private selectElipse = function() {
this.tipo = 2;
this.funcionSTR = "elipse(x,y,radioX, radioY, rotacion_en_grados)";
}
private selectCircle = function() {
this.tipo = 3;
this.funcionSTR = "circulo(x,y,radio)";
}
private elipse = function(x: number, y: number, radiusX: number, radiusY: number, rotation: number) {
return [x, y, radiusX, radiusY, rotation];
}
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(event){
if (!event.ctrlKey && !event.shiftKey && !event.altKey){
if (/^[a-zA-Z\-\_]$/.test(event.key))
this.codemirror.instance.showHint(event);
if(this.copiaNombreArchivo!=this.archivo.nombre || this.copiaContenidoArchivo != this.archivo.contenido){
this.modificado = true;
}else{
this.modificado = false;
}
}
if (event.ctrlKey && event.shiftKey && event.key == 'K')
this.codemirror.instance.toggleComment();
}
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);
});
}else{
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);
});
}
}
}
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'){
this.haskellService.editarArchivo(this.archivo.id, this.archivo)
.subscribe(
archivo => {
this.archivo = archivo;
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);
}
this.lockSaveButton();
this.ghciService.loadFile(archivo.id,idList);
},
error => {
this.notifService.error(error);
});
}else{
var list = this.sessionService.getDependencias(),
idList = [];
for(var l in list){
idList.push(list[l].id);
}
if(!idList.some(id => id ==this.archivo.id)){
idList.push(this.archivo.id);
}
this.ghciService.loadFile(this.archivo.id,idList);
}
}else{
this.haskellService.crearArchivo(this.archivo)
.subscribe(
archivo => {
this.archivo = archivo;
this.lockSaveButton();
this.ghciService.loadFile(archivo.id,[]);
},
error => {
this.notifService.error(error);
});
}
}
this.ghciService.focusConsole();
}
download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:application/octet-stream,' + encodeURIComponent(text));
element.setAttribute('download', filename+ ".mf");
element.style.display = 'none';
document.body.appendChild(element);
element.click();
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);
}
}
reiniciarInterprete(){
this.ghciService.reiniciarInterprete();
}
toggleConsole(){
this.consolaVisible = !this.consolaVisible;
}
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)=>{
if(isConfirmed) {
//codeMirrorRef.options.readOnly = false;
//componentRef.editDialogFired = true;
}
});
}
buildTreeFromList (archivos){
this.sessionService.setArchivosList(archivos);
var root :Archivo;
for(var a in archivos){
var arch = archivos[a];
if(arch.padreId===-1){
root = arch;
}
}
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);
}
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]);
}
}
return root;
}
getArchivos(id,archivos){
return archivos.filter(
function(a){
return a.padreId === id;
});
}
onChangeTab(event) {
if (event.nextId == 'FigurasBtn3D') {
this.graph3DComp.onActivate();
}
}
}