Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • matefun/Frontend
  • felipe.parodi/Frontend
2 results
Show changes
Showing
with 626 additions and 656 deletions
/** /**
* This barrel file provides the export for the lazy loaded BlankpageComponent. * This barrel file provides the export for the lazy loaded BlankpageComponent.
*/ */
export * from './canvas.component'; export * from "./canvas.component";
export * from './canvas.routes'; export * from "./canvas.routes";
export class FunctionServicesResult {
code: number;
msg: string;
}
\ No newline at end of file
export class Function {
id: number;
name: string;
str: string;
}
\ No newline at end of file
<div class="panel panel-success">
<div class="card">
<h4 class="card-header">Mis funciones</h4>
<div class="card-block">
<ul class="list-group list-group-flush">
<li *ngFor="let f of _funcs" class="list-group-item">
<div class="card-block">
<div class="checkbox">
<label for="checkbox">
{{f.str}}
</label>
</div>
<div class="pull-right action-buttons">
<button (click)="modifyFunc(f.name)" class="btn btn-success">
<i class="fa fa-pencil" ></i>
</button>
<button (click)="removeFunc(f.name)" class="btn btn-success">
<i class="fa fa-remove" ></i>
</button>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="panel-footer">
<div class="row">
<div class="col-md-12">
<div class="input-group input-group">
<input [(ngModel)]="_newFunction.str" placeholder="nueva función" class="form-control" >
<span class="input-group-btn">
<button class="btn btn-primary" [disabled]="this._newFunction.str==''" type="button" (click)="handleAddFunction()">Cargar función</button>
</span>
</div>
</div>
</div>
<!-- <button (click)="popToast()">pop toast</button> -->
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { Function } from './function';
import { FunctionServices } from './functions.service';
import { FunctionServicesResult } from './function.services.result';
//import {ToasterModule, ToasterService,ToasterConfig} from 'angular2-toaster';
@Component({
selector: 'functions',
templateUrl: './functions.component.html',
styleUrls: ['./functions.css'],
providers: []
})
export class FunctionsComponent implements OnInit {
title = 'Módulo de funciones';
_funcs: Function[];
_newFunction :Function = {'id':2,'name':'g','str':'g(x)=x+41'};
//_toasterService: ToasterService;
//
constructor(private functionServices: FunctionServices) {
// this._toasterService = toasterService;
}
getFuncs(): void {
this.functionServices.getAll().then(result => this._funcs = result);
}
removeFunc(name:string): void{
this.functionServices.delete(name);
this.getFuncs();
}
modifyFunc(name:string): void{
var f = this.functionServices.get(name);
console.log(f);
this._newFunction = f;
}
ngOnInit(): void {
this.getFuncs();
}
handleAddFunction(): void{
if(this._newFunction.str.length>4){
this._newFunction.id =1;
}
var _f:Function = {'id':this._newFunction.id,'name':this._newFunction.str.split('(')[0], 'str':this._newFunction.str};
var response = this.functionServices.add(_f);
this._newFunction.id=0;
this._newFunction.name='';
this._newFunction.str = '';
}
}
.trash {
color:rgb(209, 91, 71);
}
.flag {
color:rgb(248, 148, 6);
}
.panel-body { padding:0px; }
.panel-footer .pagination { margin: 0; }
.panel .glyphicon,.list-group-item .glyphicon { margin-right:5px; }
.panel-body .radio, .checkbox { display:inline-block;margin:0px; }
.panel-body input[type=checkbox]:checked + label { text-decoration: line-through;color: rgb(128, 144, 160); }
.list-group-item:hover, a.list-group-item:focus {text-decoration: none;background-color: rgb(245, 245, 245);}
.list-group { margin-bottom:0px; }
\ No newline at end of file
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { FunctionsComponent } from './functions.component';
//import {ToasterModule, ToasterService} from 'angular2-toaster';
@NgModule({
imports: [
CommonModule,
FormsModule
//,ToasterModule
],
declarations: [
FunctionsComponent
],
exports:[FunctionsComponent]
})
export class FunctionsModule { }
import { Route } from '@angular/router';
import { FunctionsComponent } from './index';
export const FunctionsRoutes: Route[] = [
{
path: 'functions',
component: FunctionsComponent
}
];
import { Injectable } from '@angular/core';
import { Function } from './function';
import { FunctionServicesResult }from './function.services.result';
const regex = /^\w+\(((\w+(\s)*)?|((\w+(\s)*),(\s)*)*(\w+(\s)*))\)(\s)*=(\s)*$/g;
@Injectable()
export class FunctionServices {
getAll(): Promise<Function[]>{
return Promise.resolve(this.allfunctions);
};
delete(functionName:String):void{
console.log("deleting" + functionName);
this.allfunctions = this.allfunctions.filter(function(f){return f.name!=functionName});
};
add(f: Function): FunctionServicesResult{
console.log(f);
if(f.id!=0){
this.allfunctions.push(f);
var result : FunctionServicesResult = {'code':200,'msg':'' };
} else {
var result : FunctionServicesResult = {'code':500,'msg':'Función no válida' };
}
return result;
};
get(name:string){
try{
return this.allfunctions.filter(function(f){return f.name===name})[0];
} catch(err){
return null;
}
};
getToPlot(name:string){
console.log("Functions: ");
console.log(this.allfunctions);
try{
return this.allfunctions.filter(function(f){return f.name===name})[0].str.split("=")[1];
} catch(err){
return null;
}
};
private allfunctions : Function[] = [{'id':12,'name':'f', 'str':'f(x)=x+5'}];
}
\ No newline at end of file
import { Component } from '@angular/core';
import { DialogComponent, DialogService } from "ng2-bootstrap-modal";
import { Archivo, Evaluacion } from '../../shared/objects/archivo';
export interface ConfirmModel {
cedula:string;
archivo: Archivo;
parentContext: any;
}
@Component({
selector: 'confirm',
template: `<div class="modal-dialog" style="margin-top:100px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Calificar entrega</h5>
<button type="button" class="close" (click)="close()" style="margin-left:8px;">&times;</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="message-text" class="form-control-label">Calificacion (1-100):</label>
<input type="number" class="form-control" [(ngModel)]="nota" min=1 max=100 [ngModelOptions]="{standalone: true}" >
</div>
<div class="form-group">
<label for="message-text" class="form-control-label">Detalle:</label>
<textarea class="form-control" id="message-text" [(ngModel)]="descripcion" [ngModelOptions]="{standalone: true}" ></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="cancel()">Cancelar</button>
<button type="button" class="btn btn-success" (click)="confirm()">Calificar</button>
</div>
</div>
</div>`
})
export class CalificarEntrega extends DialogComponent<ConfirmModel, boolean> implements ConfirmModel {
descripcion: string = "";
cedula: string;
archivo: Archivo;
nota: number = 1;
parentContext: any;
constructor(dialogService: DialogService) {
super(dialogService);
}
ngOnInit() {
if(this.archivo.evaluacion){
this.descripcion = this.archivo.evaluacion.descripcion;
this.nota = this.archivo.evaluacion.nota;
}
}
confirm() {
var evaluacion = new Evaluacion();
evaluacion.cedulaDocente = this.cedula;
evaluacion.descripcion = this.descripcion;
evaluacion.nota = this.nota;
if(this.nota>0 && this.nota<100){
this.parentContext.haskellService.calificarArchivo(this.archivo.id,evaluacion )
.subscribe(
evaluacion => {
this.parentContext.notifService.success("Archivo evaluado");
this.archivo.evaluacion = evaluacion;
this.close();
},
error => {
this.parentContext.notifService.error(error);
});
}else{
this.parentContext.notifService.error("Calificacion fuera de rango");
}
}
cancel(){
this.close();
}
}
\ No newline at end of file
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core";
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from "@angular/router";
import { GruposComponent } from './grupos.component'; import { GruposComponent } from "./grupos.component";
const routes: Routes = [ const routes: Routes = [{ path: "", component: GruposComponent }];
{ path: '', component: GruposComponent }
];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule] exports: [RouterModule],
}) })
export class GruposRoutingModule { } export class GruposRoutingModule {}
\ No newline at end of file
<notificacion></notificacion> <notificacion></notificacion>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-lg-5"> <div class="col-lg-5">
<label for="search">Nombre del archivo:</label> <label for="search">Nombre del archivo:</label>
<div class="input-group"> <div class="input-group">
<!--[(ngModel)]=filtroNombre--> <!--[(ngModel)]=filtroNombre-->
<input type="text" class="form-control" id="search" > <input type="text" class="form-control" id="search" />
<span class="input-group-addon fa fa-search"> <span class="input-group-addon fa fa-search"> </span>
</span> </div>
</div>
</div>
</div> </div>
<div class="row" style="margin-top: 20px"> </div>
<div class="col-lg-5"> <div class="row" style="margin-top: 20px">
<div class="card" *ngIf="grupoSeleccionado == undefined"> <div class="col-lg-5">
<div class="card-header"> <div class="card" *ngIf="grupoSeleccionado == undefined">
<div *ngIf="grupoSeleccionado==undefined">Grupos</div> <div class="card-header">
</div> <div *ngIf="grupoSeleccionado == undefined">Grupos</div>
<div class="card-block" *ngIf="grupoSeleccionado == undefined"> </div>
<div class="row listado-grupos" style="min-height: 100px; overflow-y: scroll;"> <div class="card-block" *ngIf="grupoSeleccionado == undefined">
<div class="loading" *ngIf="loading"> <div
<div class="loading-bar"></div> class="row listado-grupos"
<div class="loading-bar"></div> style="min-height: 100px; overflow-y: scroll"
<div class="loading-bar"></div> >
<div class="loading-bar"></div> <div class="loading" *ngIf="loading">
</div> <div class="loading-bar"></div>
<div *ngFor="let grupo of grupos" (click)="seleccionarGrupo(grupo)" class="col-sm-3 col-4 " style="text-align: center;"> <div class="loading-bar"></div>
<i class="fa fa-users" style="font-size: 3em; cursor: pointer;color: #f95e5e;" aria-hidden="true"></i> <div class="loading-bar"></div>
<p style="cursor: pointer;">{{grupo.grado + '°' + grupo.grupo+" - "+grupo.anio}}</p> <div class="loading-bar"></div>
</div> </div>
</div> <div
</div> *ngFor="let grupo of grupos"
(click)="seleccionarGrupo(grupo)"
class="col-sm-3 col-4 matefun-group-wrapper"
>
<i class="fa fa-users matefun-fa-user" aria-hidden="true"></i>
<p>
{{ grupo.grado + "°" + grupo.grupo + " - " + grupo.anio }}
</p>
</div> </div>
<ngb-tabset *ngIf="grupoSeleccionado" [destroyOnHide]=false> </div>
<ngb-tab title="Alumnos">
<ng-template ngbTabContent>
<div class="card">
<div>
<button class="btn btn-sm btn-secondary pull-right" style="cursor: pointer; margin-top: -35px; margin-right: 105px;" (click)="desseleccionarGrupo()" ngbPopover="Atras" data-placement="bottom" triggers="mouseenter:mouseleave">
<i class="fa fa-arrow-up"></i>
</button>
<p class="pull-right" style="margin-top: -34px; margin-right: 5px;">{{grupoSeleccionado.grado+ '°' + grupoSeleccionado.grupo+" - "+grupoSeleccionado.anio}}</p>
</div>
<div class="card-block">
<div class="row listado-grupos" style="min-height: 100px; overflow-y: scroll;">
<div *ngFor="let alumno of grupoSeleccionado.alumnos" (click)="seleccionarAlumno(alumno)" class="col-sm-3 " style="text-align: center;">
<i class="fa fa-user" style="font-size: 3em; cursor: pointer;color: #f95e5e;" aria-hidden="true"></i>
<p style="cursor: pointer;">{{alumno.apellido +', ' + alumno.nombre}}</p>
</div>
</div>
</div>
</div>
</ng-template>
</ngb-tab>
<ngb-tab title="Archivos">
<ng-template ngbTabContent>
<div class="card">
<div>
<button class="btn btn-sm btn-secondary pull-right" style="cursor: pointer; margin-top: -35px; margin-right: 105px;" (click)="desseleccionarGrupo()" ngbPopover="Atras" data-placement="bottom" triggers="mouseenter:mouseleave">
<i class="fa fa-arrow-up"></i>
</button>
<p class="pull-right" style="margin-top: -34px; margin-right: 5px;">{{grupoSeleccionado.grado+ '°' + grupoSeleccionado.grupo+" - "+grupoSeleccionado.anio}}</p>
</div>
<div class="card-block">
<div class="row listado-grupos" style="min-height: 100px; overflow-y: scroll;">
<div *ngFor="let archivo of grupoSeleccionado.archivos " (click)="seleccionarArchivo(archivo)" class="col-sm-3 col-4" style="text-align: center;">
<i class="fa fa-file-text" style="font-size: 3em; cursor: pointer;color: #ff8383" aria-hidden="true"></i>
<p style="cursor: pointer;">{{archivo.nombre}}</p>
</div>
</div>
</div>
</div>
</ng-template>
</ngb-tab>
</ngb-tabset>
</div> </div>
<div class="col-lg-7"> </div>
<div class="card" *ngIf="alumnoSeleccionado"> <ul
<div class="card-block"> ngbNav
<div class="row listadoEntregasAlumnoGrupos" style="min-height: 100px; overflow-y: scroll;" > #nav="ngbNav"
<div *ngFor="let entrega of alumnoSeleccionado.archivos" (click)="seleccionarEntrega(entrega)" class="col-sm-3 col-4" style="text-align: center;"> class="nav-tabs"
<i [ngStyle]="" class="fa fa-file-text" style="font-size: 3em; cursor: pointer;" aria-hidden="true"></i> [animation]="false"
<p style="cursor: pointer;">{{entrega.nombre}}</p> [destroyOnHide]="false"
</div> >
<div *ngIf="alumnoSeleccionado.archivos.length == 0" style="width: 100%; text-align: center;"> <li [ngbNavItem]="1">
<i style="color: rgb(220,220,220); font-size: 10em; padding: 0.1em" class="fa fa-file-text"></i> <a ngbNavLink>Alumnos</a>
<p>No hay entregas del alumno: {{alumnoSeleccionado.nombre +' '+alumnoSeleccionado.apellido }}</p> <ng-template ngbNavContent>
</div> <div class="card">
</div> <div>
<button
class="btn btn-sm btn-secondary pull-right"
style="cursor: pointer; margin-top: -35px; margin-right: 1px"
(click)="desseleccionarGrupo()"
ngbPopover="{{
'i18n.action.goBack' | translate | titleCase
}}"
placement="bottom"
triggers="mouseenter:mouseleave"
>
<i class="fa fa-arrow-up"></i>
</button>
<p
class="pull-right"
style="margin-top: -34px; margin-right: 60px"
>
{{
grupoSeleccionado.grado +
"°" +
grupoSeleccionado.grupo +
" - " +
grupoSeleccionado.anio
}}
</p>
</div>
<div class="card-block">
<div
class="row listado-grupos"
style="min-height: 100px; overflow-y: scroll"
>
<div
*ngFor="let alumno of grupoSeleccionado.alumnos"
(click)="seleccionarAlumno(alumno)"
class="col-sm-3 matefun-group-wrapper"
>
<i
class="fa fa-user matefun-fa-user"
aria-hidden="true"
></i>
<p>
{{ alumno.apellido + ", " + alumno.nombre }}
</p>
</div>
</div> </div>
</div>
</div> </div>
<div class="card" *ngIf="alumnoSeleccionado == undefined && archivoSeleccionado == undefined"> </ng-template>
<div class="card-block"> </li>
<div class="row previewArchivoNoSeleccionadoGrupos" style="min-height: 100px" > <li [ngbNavItem]="2">
<div style="width: 100%; text-align: center;"> <a ngbNavLink>Archivos</a>
<i style="color: rgb(220,220,220); font-size: 10em; padding: 0.1em" class="fa fa-file-text"></i> <ng-template ngbNavContent>
</div> <div class="card">
</div> <div>
<button
class="btn btn-sm btn-secondary pull-right"
style="cursor: pointer; margin-top: -35px; margin-right: 1px"
(click)="desseleccionarGrupo()"
ngbPopover="{{
'i18n.action.goBack' | translate | titleCase
}}"
placement="bottom"
triggers="mouseenter:mouseleave"
>
<i class="fa fa-arrow-up"></i>
</button>
<p
class="pull-right"
style="margin-top: -34px; margin-right: 60px"
>
{{
grupoSeleccionado.grado +
"°" +
grupoSeleccionado.grupo +
" - " +
grupoSeleccionado.anio
}}
</p>
</div>
<div class="card-block">
<div
class="row listado-grupos"
style="min-height: 100px; overflow-y: scroll"
>
<div
*ngFor="let archivo of grupoSeleccionado.archivos"
(click)="seleccionarArchivo(archivo)"
class="col-sm-3 col-4 matefun-group-wrapper"
>
<i
class="fa fa-file-text matefun-fa-file"
aria-hidden="true"
></i>
<p>{{ archivo.nombre }}</p>
</div>
</div> </div>
</div>
</div> </div>
</ng-template>
</li>
</ul>
<div class="card" *ngIf="archivoSeleccionado" > <div [ngbNavOutlet]="nav" class="mt-2" *ngIf="grupoSeleccionado"></div>
<div class="card-header"> </div>
<button *ngIf="tipoArchivo == 'entrega'" class="btn btn-sm btn-secondary pull-left mr-2" (click)="calificarEntrega()"> <div class="col-lg-7">
Calificar <div class="card" *ngIf="alumnoSeleccionado">
</button> <div class="card-block">
<button *ngIf="esArchivoGrupo()" ngbPopover="Cargar/Editar" data-placement="bottom" triggers="mouseenter:mouseleave" class="btn btn-sm btn-secondary pull-left mr-2" (click)="cargarArchivoCompartido()"> <div
<i class="fa fa-pencil"></i> class="row listadoEntregasAlumnoGrupos"
</button> style="min-height: 100px; overflow-y: scroll"
<div class="pull-left" > >
Nombre: {{archivoSeleccionado?.nombre}} - Creado: {{archivoSeleccionado?.fechaCreacion | date}} <div
</div> *ngFor="let entrega of alumnoSeleccionado.archivos"
</div> (click)="seleccionarEntrega(entrega)"
<codemirror class="codemirrorGrupos" [(ngModel)]="archivoSeleccionado.contenido" [config]="configCodeMirror" [ngStyle]="{'font-size': configCodeMirror.fontSize+'px'}"> class="col-sm-3 col-4 matefun-file-wrapper"
</codemirror> >
<i
class="fa fa-file-text"
style="font-size: 3em"
aria-hidden="true"
></i>
<p>{{ entrega.nombre }}</p>
</div>
<div
*ngIf="alumnoSeleccionado.archivos.length == 0"
style="width: 100%; text-align: center"
>
<i
style="
color: rgb(220, 220, 220);
font-size: 10em;
padding: 0.1em;
"
class="fa fa-file-text"
></i>
<p>
No hay entregas del alumno:
{{
alumnoSeleccionado.nombre + " " + alumnoSeleccionado.apellido
}}
</p>
</div>
</div>
</div>
</div>
<div
class="card"
*ngIf="
alumnoSeleccionado == undefined && archivoSeleccionado == undefined
"
>
<div class="card-block">
<div
class="row previewArchivoNoSeleccionadoGrupos"
style="min-height: 100px"
>
<div style="width: 100%; text-align: center">
<i
style="
color: rgb(220, 220, 220);
font-size: 10em;
padding: 0.1em;
"
class="fa fa-file-text"
></i>
</div> </div>
</div>
</div> </div>
</div>
<div class="card" *ngIf="archivoSeleccionado">
<div class="card-header">
<button
*ngIf="tipoArchivo == 'entrega'"
class="btn btn-sm btn-secondary pull-left mr-2"
(click)="mostrarModalCalificarEntrega()"
>
Calificar
</button>
<button
*ngIf="esArchivoGrupo()"
ngbPopover="Cargar/Editar"
placement="bottom"
triggers="mouseenter:mouseleave"
class="btn btn-sm btn-secondary pull-left mr-2"
(click)="cargarArchivoCompartido()"
>
<i class="fa fa-pencil"></i>
</button>
<div class="pull-left">
Nombre: {{ archivoSeleccionado?.nombre }} - Creado:
{{ archivoSeleccionado?.fechaCreacion | date }}
</div>
</div>
<lt-codemirror
class="codemirrorGrupos"
[(ngModel)]="archivoSeleccionado.contenido"
[config]="configCodeMirror"
[ngStyle]="{ 'font-size': configCodeMirror.fontSize + 'px' }"
>
</lt-codemirror>
</div>
</div> </div>
</div>
</div> </div>
<matefun-modal-calificar-entrega
cancelLabel="{{ 'i18n.action.cancel' | translate | titleCase }}"
confirmLabel="{{ 'i18n.action.qualify' | translate | titleCase }}"
[detail]="archivoSeleccionado.evaluacion?.descripcion"
detailLabel="{{ 'i18n.object.detail' | translate | titleCase }}:"
header="{{ 'i18n.msg.file.qualify' | translate }}"
[opened]="modalQualifyDeliveryOpened"
[posibleStatuses]="posibleStatusesQualifyDelivery"
[score]="archivoSeleccionado.evaluacion?.nota"
scoreLabel="{{ 'i18n.object.score' | translate | titleCase }} (0-100):"
statusLabel="{{ 'i18n.object.state' | translate | titleCase }}:"
(cancelAction)="modalQualifyDeliveryOpened = false"
(close)="modalQualifyDelivery = false"
(confirmFileQualify)="confirmFileQualify($event)"
*ngIf="modalQualifyDelivery"
>
</matefun-modal-calificar-entrega>
This diff is collapsed.
import { NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
import { FormsModule } from '@angular/forms'; import { FormsModule } from "@angular/forms";
import { GruposComponent } from './grupos.component'; import { GruposComponent } from "./grupos.component";
import { CommonModule } from '@angular/common'; import { CommonModule } from "@angular/common";
import { GruposRoutingModule } from './grupos-routing.module'; import { GruposRoutingModule } from "./grupos-routing.module";
import { DialogService } from "ng2-bootstrap-modal"; import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { NotificacionService } from '../../shared/services/notificacion.service'; import { LtCodemirrorModule } from "lt-codemirror";
import { BootstrapModalModule } from 'ng2-bootstrap-modal'; import { NotificacionModule } from "../../notificacion/notificacion.module";
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { I18nModule } from "../../shared/modules/translate/i18n.module";
import { CodemirrorModule } from 'ng2-codemirror'; import { TitleCaseModule } from "../../shared/modules/titlecase.module";
import { CalificarEntrega } from './calificarEntrega.component';
import { NotificacionModule } from '../../notificacion/notificacion.module';
@NgModule({ @NgModule({
imports: [CommonModule, GruposRoutingModule, FormsModule,BootstrapModalModule, NgbModule, CodemirrorModule,NotificacionModule], imports: [
declarations: [GruposComponent, CalificarEntrega], CommonModule,
exports: [GruposComponent], GruposRoutingModule,
entryComponents: [CalificarEntrega] FormsModule,
NgbModule,
LtCodemirrorModule,
NotificacionModule,
I18nModule,
TitleCaseModule,
],
declarations: [GruposComponent],
exports: [GruposComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}) })
export class GruposModule {}
export class GruposModule { }
This diff is collapsed.
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.