CKEditor.NET: Insert multiple events on instance


En este post vamos a ver cómo podemos incluir eventos en el editor de texto html CKEditor, en su versión de ASP.NET.

Dado que la documentación existente para este complemento es un poco escasa, creo que puede ser un post interesante, maxime cuando no he encontrado muchas referencias en la web, incluso buscando en "San Google".

Para controlar la inserción de nuevos eventos en CKEditor, tenemos la posibilidad de hacerlo vía javascript. El problema surge cuando en vez de utilizar la librería tal cual, y convertir nuestros Textarea a CKEditor, implementamos directamente los controles que ofrece la dll "CKEditor:CKEditorControl".

Tras una árdua búsqueda, y no encontrar nada más que la opción de código <INSTANCE>.config.CKEditorInstanceEventHandler, me encontré con la desagradable sorpresa de que el propio objeto CKEditorInstanceEventHandler sólo almacena el primer evento generado con la lista. Por tanto, si intentamos hacer algo similiar a esto...:

CODIGO ERRÓNEO

<INSTANCE>.config.CKEditorInstanceEventHandler = new System.Collections.Generic.List<object>();

<INSTANCE>.config.CKEditorInstanceEventHandler.Add(new object[] { "instanceReady", "MyInstanceReadyFunction" });

<INSTANCE>.config.CKEditorInstanceEventHandler.Add(new object[] { "paste", "MyPasteFunction" });

...observaremos que el segundo evento no se lanza jamás.

Llegados a este punto,sólo nos queda pensar cómo instanciar todos nuestros eventos, sobre todo cuando el control CKEditor:CKEditorControl lanza exclusivamente el primero.

Sorprendentemente la respuesta no podía ser más sencilla. Puesto que podemos implementar los eventos por javascript, lo único que nos restaría por hacer, es generar una función Javascript que realice ese trabajo, recibiendo como parametro el control a inicializar.

Con este fragmento de código, y modificandolo en funcion de nuestras necesidades, podremos añadir tantos eventos como sea necesario.

CODIGO ASP.NET
<INSTANCE>.config.CKEditorInstanceEventHandler = new System.Collections.Generic.List<object>();
<INSTANCE>.config.CKEditorInstanceEventHandler.Add(new object[] { "instanceReady", "MyInstanceReadyFunction" });

CÓDIGO JAVASCRIPT
/**
* Funcion que inicializa la instancia CKEditor, a partir del evento instanceReady
* @param {Event} Objeto a instanciar
*/
function MyInstanceReadyFunction (evt) {
    var maxLength = 255;
    evt.editor.on('paste', function (e) {
        e.data.html = strip_tags(e.data.html, tagsPermitidas);
        checkLength(e, maxLength);
    });
    evt.editor.on('key', function (e) {
        checkLength(e, maxLength);
    });
}

Concretamente en este ejemplo, la inicialización de eventos incluye:
· 'paste': que controla el texto que se va a pegar, y suprime los tags HTML que no permito en dicho campo,
· 'key': que controla la escritura de texto, hasta llegar a un nº máximo de caracteres establecidos.

Evidentemente, incluir o modificar éstos, es cuestión de cambiar el código.

Incluyo este ejemplo con 'paste' y 'key' porque sé que hay muchos ejemplos para incluir una limitación de caracteres en CKEditor, pero no es fácil encontrarlos.

Respecto a la función para eliminar los tags no permitidos, os adjunto el código un poco más abajo.

FUNCION PARA ELIMINAR TAGS NO PERMITIDOS

/* Variable global que controla los tags permitidos en la edición */
var tagsPermitidas = '<div><p><span><h1><h2><h3><h4><h5><h6><li><ul><ol><b><strong><small><u><i><em><a><sup><sub>';
/**
* Devuelve un codigo HTML limpio de etiquetas, salvo las permitidas
* @param {String} Cadena de entrada
* @param {String} Tags de apertura permitidos
* @return {String} Cadena limpia
*/
function strip_tags(input, allowed) {
    allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
    var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
       commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;

    return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
        return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
    });
}

CODIGO PARA CONTROLAR EL TAMAÑO MÁXIMO checkLength: 

Referencia en el blog dotRody CKEditor Maximun Length.

NOTA: Tener en cuenta que el límite máximo indicado en la instancia, hace referencia al texto, y no al conjunto total con sus estilos y tags html. Si no se tiene en cuenta la dimensión del texto con éstos parámetros, nos podemos llevar una mala sorpresa al insertarlo en la DDBB, donde tendremos un error de 'Overflow'.

Espero que os sirva y os ahorre un buen tiempo de búsqueda.

Un saludo a todos!
[...]

Leer más >>

CODE SNIPPET: Advanced popup window

En este CODE SNIPPET, continuando y mejorando el anterior ‘Basic popup window’, voy a mostrar cómo se genera un plugin utilizando javascript y jquery, que muestre popup’s dinámicos.

Partiendo del código del post anterior y con el fin de reutilizar código para popup multiples, vamos a agrupar todo el contenido en un plugin que será el encargado de rellenar el popup e incluir toda la funcionalidad en un único archivo. De esta forma, tendremos la ventaja de poder generar popup’s de forma sencilla sin necesidad de repetir código.
Este pequeño plugin puede mostrar dos tipos de popups: bien una alerta estática de texto, bien un contenido de una capa.

Para ello, generaremos el código básico de un plugin, al que iremos añadiendo los distintos fragmentos de código, hasta completar el archivo.

Lo primero que haremos, será añadir los atributos base de nuestro plugin. Recordad que podemos ampliarlos, en función de nuestras necesidades. Por tanto, la primera aproximación queda así:


jQuery.fn.ShowPopup = function (options) {


    // atributos por dedecto, que extienden a options

    var settings = $.extend({

        cerrarText: '[X]', //texto para el botón de cerrar

        isAlert: false, //atributo que indica si es un popup de alerta o de objeto
        AlertText: undefined, //texto a mostrar en caso de tener una alerta estática
        onClose: undefined //callback a ejecutar tras cerrar el popup
    }, options);
    // creamos los metodos
    var methods = {};
    //creamos el plugin
    return this.each(function () {});

};

Como se puede ver, he incorporado una pequeña funcionalidad que puede ser útil, y es, la opción de pasar como parámetro una función para ejecutar cuando se cierra el popup. 

Una vez creados los atributos, procedemos a generar los métodos que utilizaremos para dar funcionalidad a nuestro plugin. Así, implementaremos tres métodos: open, close y destroy, encargados de abrir, cerrar y destruir el popup respectivamente:

    // creamos los metodos
    var methods = {
        open: function () { $('BODY &gt; .popup').fadeIn(300); },
        close: function () { $('BODY &gt; .popup').fadeOut(300, methods.destroy); },
        destroy: function (content) {
            //destruimos cualquier popup previo
            if ($('BODY &gt; .popup').length) $('BODY &gt; .popup').stop().hide().remove();
        }
    };

Con el método destroy, nos aseguramos de que sólo haya un popup en el body. La ventaja de utilizar estos métodos, reside en la facilidad para insertar animaciones y efectos a nuestro gusto, en las funciones de apertura y cierre (en este caso, unos simples efectos de alpha).

Por último, incorporamos el código que junta ambas partes y genera finalmente el popup:

//creamos el plugin
return this.each(function () {
    methods.destroy(); //destruimos cualquier popup previo

    //montamos las capas
    var wrapper = $('<div class="popup">');
    var popup = $('<div class="popup_window">');
    var closed = $('<div class="popup_close">');
    var content = $('<div class="popup_contenido">');

    //incorporamos los elementos al pop, y el pop al wrapper
    popup.append(closed).append(content);
    wrapper.append(popup);

    //incluimos el popup en el body
    $('body').append(wrapper);

    //inicializamos el boton cerrar
            
    closed.html(settings.cerrarText);
    closed.click(methods.close);
    if (jQuery.isFunction(settings.onClose)) closed.bind('click', settings.onClose);

    //incluimos el contenido (comprobamos si es texto o un objeto)
    if (settings.isAlert) {
    var alert_content = $('<div>' + settings.AlertText + '</div>');
    alert_content.addClass('contenido_alerta');
    content.html(alert_content);
    }
    else {
    //'Clone' de capa para no borrarla al 'destroy'
    var dinamic_content = $(this).clone();
    content.html(dinamic_content.show().addClass('contenido'));
    }

    //mostramos
    methods.open();
});

Sólo quedaría añadir los css y el código html para efectuar las llamadas que, a modo de ejemplo podrían ser:

<a href="#" onclick="$('#mipop').ShowPopup();">
Mostrar CAPA como popup
</a>


O con parámetros:

<a href="#" onclick="$('BODY').ShowPopup({cerrarText: 'CERRAR', isAlert:true, AlertText:$(this).html(), onClose:function(){alert('wow!');} });">
Mostrar alerta de nuevo un texto como popup, y avisar de su cierre.
</a>


En el siguiente enlace, podéis encontrar todo el código fuente de ejemplo.
Source Code

P.D: Mejorar y evolucionar este código queda ahora como algo más sencillo. Desde incluir varios botones, hasta personalizar todo el comportamiento. Sólo es cuestión de probar, fallar y al final encontrar qué es lo que mejor se adapta a nuestras necesidades.

Como siempre, espero que os sirva.
¡Un saludo a todos!
[...]

Leer más >>

CODE SNIPPET: Basic popup window


Con esta entrada, pretendo iniciar una serie de post que, como el propio título indica, sea un conjunto de publicaciones con pequeños fragmentos de código que se suelen utilizar en la mayoría de los proyectos. En este caso, el uso de un popup basico en html y javascript

Si al igual que yo, muchos de vosotros reutilizáis vuestro código en múltiples proyectos, habréis descubierto lo engorroso que es ir buscando en las carpetas de las aplicaciones archivadas, hasta descubrir dónde se encuentra el dichoso código para después copiarlo y pegarlo en el nuevo.

Pues bien, intentando evitar la pérdida de ese tiempo precioso, intentaré ir subiendo poco a poco ese tipo de  código reutilizable, para tener aquí una referencia en dónde buscarlo.

Y como inicio de esta sección os indicaré el código para obtener de forma sencilla un popup general, que muestre un contenido concreto, en forma de ventana emergente por encima del contenido de una página.

CODIGO HTML

Para empezar necesitaremos el fragmento de código HTML, que genere las capas correspondientes tanto en ubicación como en contenido.

<div id="MiPopup" class="popup">
    <div class="popup_window">
        <div class="popup_close" title="Cerrar onclick="CierraPopup(this);">[cerrar]</div>
        <div class="popup_contenido"></div>
    </div>
</div>

Básicamente, la idea de este fragmento es la construcción de una capa base (‘popup’), que ocultará todo el contenido bajo ella, y otra centrada (‘popup_window’), con unas dimensones concretas que será la que muestre el contenido.

Las dos capas internas de la ventana centras son para separar el contenido a mostrar (‘popup_contenido’) del botón para cerrar el popup (‘popup_cerrar’).

NOTA: El tag ‘onclick’ de la capa ‘popup_close’ será el encargado de ocultar el popup al pulsarse. La función para realizarlo, la puedes obtener más abajo.

CODIGO CSS

Una vez que tenemos la estructura general montada, debemos darle el estilo CSS. Aquí está el código de ejemplo:

.popup {
    background-color:rgba(0,0,0, 0.5);
    display: none;
    height: 100%;
    left: 0;
    position: fixed;
    top: 0;
    width: 100%;
    z-index: 100000;
    color:#fff;
}
.popup_window {
    background-color:#333;
    box-shadow:0 0 12px 12px #FFF;
    height: 300px;
    margin: -150px auto 0;
    position: relative;
    top: 50%;
    width: 400px;
 }
.popup_window > .popup_close {
    cursor:pointer;
    position:absolute;
    right: 10px;
    top: 10px;
}
.popup_window > .popup_contenido {
    margin: 0 auto;
    text-align: center;
    width: 100%;
}

Evidentemente éste es solo un ejemplo, y en nuestro caso hemos generado una ventana centrada de 400x300 pixels. Cualquier parámetro puede ser modificado al gusto, e incluso podemos insertar imágenes de background para personalizar aún más el resultado.

CODIGO Javascript

Por último, sólo nos faltaría implementar el javascript para que la ventana se muestre y se oculte con la interacción del usuario. Para esto, simplemente añadimos el siguiente javascript:

<script type="text/javascript">
    function MuestraPopup(pop) {
        document.getElementById(capa).style.display = 'show';
    }
    function CierraPopup(pop_close) {
        pop_close.parentNode.parentNode.style.display = 'none';
    }
</script>

NOTA: Recomiendo utilizar algún framework de javascript, por la comodidad de programación, estructuración y modelado del código.

Simplemente restaría añadir en la página HTML un botón para mostrar dicho popup. Dado que se podría hacer de muchas formas, os indico la más simple, que sería añadir un link en la página:
<a href="#" onclick="MuestraPopup('MiPopup'); return false;">Abrir Pop-up</a>

NOTA: El return false; lo añado para evitar que se ejecute la navegación del href y se recargue la página.

Con este pequeño fragmento de código, ya podéis tener vuestras propias ventanas informativas, sin peligro de bloqueos de popup, ni nada por el estilo. Y además, totalmente personalizables.

Espero que os ayude.
¡Un saludo a todos!



[...]

Leer más >>

Responsive Aeon, framework para Responsive Design


Hoy a caído en mis manos un framework para diseñar proyectos web, de forma que sean adaptables a cualquier dispositivo sobre el que se esté visualizando. Como reza el título, se trata de ResposiveAeon.

Como en la mayoría de los casos mediante la descarga de unos archivos básicos, podremos diseñar nuestros proyectos web sin preocuparnos del dispositivo.

Al igual que otros sistemas de maquetación, está basado en columnas, pero lo que me ha llamado la atención es lo simple de su estructura y programación.

Aquí están algunas de sus características:
• Distribuye los contenidos en columnas, con un ID propio y el CSS se encarga del resto.
• Permite el uso de etiquetas HTML5.
• Establece la carga de archivos de forma paralela, optimizando la visualización de la página.
• Tiene plantillas para diseñar contenidos, que se instalan en Photoshop, Illustrator,...
• Por supuesto, funciona en todos los navegadores.

Y cómo no, está a nuestra disposición en GitHub.

Creo que va siendo hora de probarlo.
¿Y vosotros, qué opinais de él?

Referencias:
http://www.newaeonweb.com.br/responsiveaeon/
GitHub Proyect
Demo
[...]

Leer más >>

Internet Explorer se suma a las Actualizaciones Automáticas 'IMPORTANTES'

He de reconocer que aunque la siguiente noticia es antigua, tiene la suficiente entidad como para remarcarla ahora que ha llegado a mis manos. Tal y como reza el título, Microsoft ha decidido 'animar a sus usuarios' ha actualizar su versión del navegador a las últimas versiones, dependiendo de los Sistemas Operativos instalados en sus máquinas - con acutalizaciones automáticas IMPORTANTES

Esto es una gran noticia para los desarrolladores que al igual que yo, tienen que realizar trabajos web, y se ven forzados a efectuar el mismo trabajo con soporte visual para distintos navegadores .... entre los que se incluye I.Explorer 7.

El problema de tener que trabajar para diferentes versiones de un mismo navegador, sobre todo con Internet Explorer, es que las diferencias con respecto a las nuevas versiones son tan grandes, que casi nos vemos obligados a realizar distintas implementaciones para un mismo proyecto web, con la consiguiente carga de trabajo y tiempo.

Pues bien, parece que Microsoft ha oido nuestras plegarias, y aquellos clientes que aún trabajen con navegadores antiguos, tendrán una nueva actualización en su Windows Update, de caracter IMPORTANTE (esto es, de actualización directa), para que instalen la última versión del navegador en sus S.Operativos.

Tal y como indica Paul Irish en su blog, los usuarios tendrán unas actualizaciones para el navegador de la siguiente manera:
· Usuarios de Windows XP, verán la opción de actualización de su navegador de IE6 - IE7 a IE8,
· Usuarios de Windows Vista y W7, verán una actualización a IE9

Esperemos que con este sistema, nuestros clientes y usuarios web se actualicen a las versiónes más modernas del navegador y puedan disfrutar de los beneficios que ofrecen éstos, como JQuery, animaciones, HTML5, CSS3... etc.

NOTITA APARTE:
Por cierto, JQuery lanzó un aviso hace poco.... la versión 2.0 de sus librerías ya no tendrá soporte para  las versiones de IE6,7 y 8.
Pero esa es otra historia... (que puedes leer aquí)

¡Un saludo a todos!

Y como siempre, las referencias:
Windows Team Blog
Paul Irish Blog
[...]

Leer más >>

OnClick Event not fired first in custom control

Tal y como indica el título de este post, voy a explicar porque a veces no se ejecuta el evento 'OnClick' a la primera, dentro de nuestros Custom controls, y sí a la segunda.

El problema supuesto se puede reproducir insertando en una página aspx, varios controles personalizados, que a su vez tienen subcontroles hijos personalizados con botones que deben ejecutar un postback a la página padre.

Contemos con el supuesto inicial:
· Una página padre, con un UpdatePanel de Ajax.
· Un control principal, que se inserta 5 veces en la página padre.
· Unos controles hijos que se insertan dinámicamente en el control padre (segun la interaccion del usuario), hasta un total de 3.
· Estos controles hijos tienen un botón que permite eliminarlos de la lista del control padre.

El proceso es el siguiente:
Cuando se carga la página principal, el usuario puede añadir los controles padre según le convenga. Además, según se van cargando los controles padres, el usuario puede ir insertando en ellos, los controles hijos. Si en algún momento se encuentra en la necesidad de borrar un subcontrol hijo de la lista de uno de los padres, debe pulsar su botón para eliminarlo... y es en este punto donde falla el sistema si se han añadido más de un control hijo.

Después de mucho buscar en "San Google" (más adelante pondré algunas referencias) en la mayoría de los supuestos, se hablaba de un conflicto con los postback, y la carga de los controles de forma asíncrona. O bien, trataban el tema desde la generación de los controles en el Page_Load.

Teniendo en cuenta las anteriores soluciones y confirmando que todo estaba en su sitio, encontré mi propia solución y tuve que darme golpes contra la pared porque se trataba de un tema tan simple como abrumador.

SOLUCION:

El propio compilador de ASP.NET, genera un ID automático y único para cada componente insertado en la página, sin importar cuando o donde se genere. Ahora bien, si se insertan dinámicamente los controles, es posible que por el orden de inserción, dichos controles no contengan el mismo ID entre un postback y otro.

Esto supone, que si insertamos un primer control e inmediatamente después, realizamos un postback sobre él, el sistema es capaz de reconocer de dónde viene, y por tanto ejecutarlo. Pero si por el contrario, insertamos un primer elemento y posteriormente un segundo, es posible que los ID autogenerados entren en conflicto y al intentar realizar un postback desde el primero, éste no encuentre el ID inicial y no ejecute el evento.

NOTA CURIOSA: En este punto la página ha vuelto a reenviarse y cada subcontrol se ha vuelto a generar con un nuevo ID. Por tanto si se vuelve a ejecutar el postback del botón, esta vez SÍ encontrará el control y lo realizará correctamente.

COMO EVITAR ENTONCES este problema...
Tan sencillo como estúpido. Asigna directamente un ID único a tu subcontrol cuando lo generes. De esta forma mantendrá siempre un identificador unico por muchos controles que insertes en la página.

Entiendo que algunos me podréis decir que es "Good Practice" asignar directamente el ID y sí, así es. Pero el problema es que muchas veces necesitamos trabajar tan rápido, y estamos tan acostumbrados a las bondades de Visual Studio, que nos olvidamos de estos pequeños detalles (tan importantes).

Así que no lo olvides, toma como buena práctica identificar cada control que generes con un ID propio, y evitarás este molesto ¿problemilla?.

Espero que os ayude.
¡Un saludo a todos!

Referencias:
StackOverflow Questions: 2765815
PostBack forced on UpdatePanel
Programmatically adding ASP.net User Control to Partial Page Update / UpdatePanel
[...]

Leer más >>

EVENTO: Webcast MSDN - ASP.NET MVC

Webcast MSDN: ¿TODAVÍA NO CONOCES ASP.NET MVC?



Gracias a Microsoft y sus World Wide Events, en colaboración con DesarrolloWeb, tenemos a nuestra disposición la posibilidad de participar en un gran evento de ASP.NET.

Si aún no conocéis ASP.NET MVC, o queréis conocer más sobre esta nueva tecnología, no debéis dejar pasar esta oportunidad de participar en este evento.

La fecha del mismo es el Miércoles, 14 de marzo de 2012 a las 19:00 

Para apuntaros y tener toda la información de primera mano, entrar en Webcast MSDN: ¿TODAVÍA NO CONOCES ASP.NET MVC?


desarrolloweb.com
[...]

Leer más >>