/**
* @File mm.js Implementación del MM
* @author José Luis Molina Soria
* @version 20130520
*/
MM = function (mm) {
/**
* @prop {number} idNodos Identificador de nodos. Cada vez que se crea un nodo se
* le asigna un nuevo identificador
* @memberof MM
* @inner
*/
var idNodos = 1;
/**
* @prop {MM.UndoManager} undoManager es el manejador de acciones hacer/deshacer (undo/redo)
* @memberof MM
* @inner
*/
mm.undoManager = new MM.UndoManager(10);
/**
* @prop {MM.PubSub} eventos Gestor de eventos del Mapa mental
* @memberof MM
* @inner
*/
mm.eventos = new MM.PubSub();
/**
* @desc Sobreescritura del método "equal" del MM.Arbol. La comparación se realiza a
* nivel de identificador.
* @method elementEqual
* @memberof MM
* @inner
*/
MM.Arbol.prototype.elementEqual = function ( id ) {
return id === this.elemento.id;
};
/**
* @desc Genera un nuevo Mapa mental. Eliminar el Mapa mental existente hasta el momento.
* Resetea el contador de nodos.
* @param {String} ideaCentral Texto de la idea central. Por cefecto 'Idea Central'
* @method nuevo
* @memberof MM
* @instance
*/
mm.nuevo = function ( ideaCentral ) {
if ( this.arbol ) {
this.ponerFoco ( this.arbol );
for ( var i = 0; i < this.arbol.hijos.length; i ) {
this.next();
this.borrar();
}
this.eventos.on ( 'nuevo/pre' );
}
idNodos = 1;
/**
* @prop {MM.Arbol} arbol Arbol-eneario que representa al Mapa mental.
* @memberof MM
* @inner
*/
this.arbol = this.foco = new MM.Arbol(
{ id: idNodos++,
texto: ideaCentral || 'Idea Central',
plegado: false,
nodo: null }
);
this.ponerFoco ( this.arbol );
this.eventos.on ( 'nuevo/post' );
}.chain();
/**
* @desc Añade un nodo al Mapa mental. Se añade un hijo al elemento activo (que tiene el foco).
* Todos los nodos del árbol tiene como elemento un id, texto y un nodo (instancia de
* MM.NodoSimple o MM.Globo. Es Chainable, esto nos permite realizar operaciones encadenadas.
* Por ejemplo, MM.add('Abuelo').add('Padre').add('Hijo').add('Nieto');
* @param {string} texto Texto del nuevo nodo. Valor por defecto "Nuevo".
* @return {MM} Al ser Chainable devuelve this (MM).
* @method add
* @memberof MM
* @instance
*/
mm.add = function ( texto ) {
texto = texto || "Nueva idea";
var nuevo = new MM.Arbol ( { id: idNodos++, texto: texto, plegado: false, nodo: null } );
this.foco.hijos.push ( nuevo );
this.undoManager.add(new MM.comandos.Insertar(this.foco.elemento.id, nuevo.elemento.id, texto) );
this.eventos.on ( 'add', this.foco, nuevo );
nuevo = null;
}.chain();
/**
* @desc Borra el nodo que tiene el foco. Implementael patrón Chainable.
* @return {MM} Al ser Chainable devuelve this (MM).
* @method borrar
* @memberof MM
* @instance
*/
mm.borrar = function () {
if ( this.arbol === this.foco ) {
this.nuevo();
return;
}
var borrar = this.foco;
this.padre();
this.arbol.borrar ( borrar.elemento.id );
this.undoManager.add(new MM.comandos.Borrar(this.foco, borrar));
this.eventos.on ( 'borrar', this.foco, borrar );
borrar = null;
}.chain();
/**
* @desc Cambia el foco a primer hijo del nodo que tiene actualmente el foco.
* @return {MM} Al ser Chainable devuelve this (MM).
* @method next
* @memberof MM
* @instance
*/
mm.next = function () {
if ( this.foco.ordenNodo() !== 0 ) {
this.eventos.on ( 'next', this.foco, this.foco.hijos[0] );
this.ponerFoco ( this.foco.hijos[0] );
}
}.chain();
/**
* @desc Cambia el foco al padre del nodo activo.
* @return {MM} Al ser Chainable devuelve this (MM).
* @method padre
* @memberof MM
* @instance
*/
mm.padre = function () {
if ( !this.foco ) { return; }
var padre = this.arbol.padreDe ( this.foco.elemento.id );
if ( padre !== null ) {
this.eventos.on ( 'padre', this.foco, padre );
this.ponerFoco ( padre );
}
padre = null;
}.chain();
/**
* @desc Cambia el foco al siguiente hermano del nodo actual. Si llega al último
* siguiente hermano se entiende que es el primero
* @return {MM} Al ser Chainable devuelve this (MM).
* @method nextHermano
* @memberof MM
* @instance
*/
mm.nextHermano = function () {
var padre = this.arbol.padreDe ( this.foco.elemento.id );
if ( padre === null ) { return; }
for ( var i = 0; i < padre.hijos.length; i++ ) {
if ( padre.hijos[i].elementEqual ( this.foco.elemento.id ) ) {
if ( i === padre.hijos.length - 1 ) {
this.eventos.on ( 'nextHermano', this.foco, padre.hijos[0] );
this.ponerFoco ( padre.hijos[0] );
} else {
this.eventos.on ( 'nextHermano', this.foco, padre.hijos[i + 1] );
this.ponerFoco ( padre.hijos[i + 1] );
}
break;
}
}
padre = null;
}.chain();
/**
* @desc Cambia el foco al hermano anterior del nodo actual. Si llega al primero
* en la siguiente llamada pasará al último de los hermanos.
* @return {MM} Al ser Chainable devuelve this (MM).
* @method prevHermano
* @memberof MM
* @instance
*/
mm.prevHermano = function () {
var padre = this.arbol.padreDe ( this.foco.elemento.id );
if ( padre === null ) { return; }
for ( var i = 0; i < padre.hijos.length; i++ ) {
if ( padre.hijos[i].elementEqual ( this.foco.elemento.id ) ) {
if ( i === 0 ) {
this.eventos.on ( 'prevHermano', this.foco, padre.hijos[padre.hijos.length - 1] );
this.ponerFoco ( padre.hijos[padre.hijos.length - 1] );
} else {
this.eventos.on ( 'prevHermano', this.foco, padre.hijos[i - 1] );
this.ponerFoco ( padre.hijos[i - 1] );
}
return;
}
}
padre = null;
}.chain();
/**
* @desc Cambia el foco al último hermano
* @return {MM} Al ser Chainable devuelve this (MM).
* @method lastHermano
* @memberof MM
* @instance
*/
mm.lastHermano = function () {
var padre = this.arbol.padreDe ( this.foco.elemento.id );
if ( padre === null ) { return; }
if ( padre.hijos.length >= 1 ) {
this.ponerFoco ( padre.hijos[padre.hijos.length - 1] );
}
padre = null;
}.chain();
/**
* @desc Pasa el foco al elemento raiz (Idea central).
* @return {MM} Al ser Chainable devuelve this (MM).
* @method root
* @memberof MM
* @instance
*/
mm.root = function () {
this.eventos.on ( 'root', this.foco, this.arbol );
this.ponerFoco ( this.arbol );
}.chain();
/**
* @desc Pone el foco en nodo (subárbol) dado.
* @param {MM.Arbol} arbol Subárbol (nodo) donde poner el foco.
* @method ponerFoco
* @memberof MM
* @instance
*/
mm.ponerFoco = function ( arbol ) {
this.eventos.on ( 'ponerFoco', this.foco, arbol );
this.foco = arbol;
};
mm.nuevo( "Idea Central" );
/**
* @prop {MM.Render} render Instancia de MM.Render. El valor por defecto es null
* y se crea en el momento de renderizar el árbol.
* @memberof MM
* @inner
*/
mm.render = null;
/**
* @desc Realiza el renderizado del Mapa mental. El renderizado se realiza ajustando el escenario al contenedor.
* Una vez llamada a esta función queda establecido el valor de la propiedad MM.render.
* @param {Element} contenedor Elemento del árbol DOM que contendrá el Mapa mental.
* @param {MM.NodoSimple|MM.Globo} claseNodo Clase de renderizado de nodo
* @param {MM.Arista|MM.Rama} claseArista Clase de renderizado de aristas
* @method renderizar
* @memberof MM
* @instance
*/
mm.renderizar = function ( contenedor, claseNodo, claseArista ) {
mm.render = new MM.Render ( contenedor, claseNodo, claseArista );
mm.render.renderizar();
};
/**
* @desc Marca el nodo actual (foco) como plegado, si no establa plegado o como
* desplegado si estaba plegado.
* @param {Boolean} plegado Si es true fuerza el plegado y si es false el desplegado
* @method plegadoRama
* @memberof MM
* @instance
*/
mm.plegarRama = function (plegado, undo) {
// - PLEGADO: Se pliega toda la herencia del nodo.
// - DESPLEGADO: Se despliega sólo el nodo en cuestión.
plegado = plegado || !this.foco.elemento.plegado;
this.foco.elemento.plegado = plegado;
var plegar = function (a) {
a.hijos.forEach(function (h) {
h.elemento.plegado = true;
plegar(h);
});
};
var desplegar = function (a) {
var aPlegado = a.elemento.plegado;
a.hijos.forEach(function (h) {
h.elemento.plegado = ( !aPlegado && h.esHoja() )?false:h.elemento.plegado;
desplegar(h);
});
aPlegado = null;
};
if ( plegado ) {
plegar(this.foco);
} else {
desplegar(this.foco);
}
this.render.dibujar(MM.arbol);
if ( !undo ) {
this.undoManager.add(new MM.comandos.Plegar(this.foco, plegado));
}
};
/**
* @desc Abre un cuadro de dialogo para seleccionar el fichero FreeMind que deseamos abrir.
* Lo carga y redendiza el nuevo Mapa mental una vez terminado la carga.
* @method cargarFreeMind
* @memberof MM
* @instance
*/
mm.cargarFreeMind = function () {
var importer = new MM.importar.FreeMind();
var susR = MM.importar.evento.suscribir("freeMind/raiz", function () {
MM.render.desuscribrirEventos();
});
var susP = MM.importar.evento.suscribir("freeMind/procesado", function () {
MM.render.renderizar();
});
var input = MM.DOM.create('input', {
'type' : 'file',
'id' : 'ficheros'
});
input.addEventListener("change", function(evt) {
if ( input.files.length !== 0 ) {
importer.cargar(input.files[0]);
}
}, false);
input.click();
};
return mm;
}(MM);