Categories
javascript juego split string unicode

Dividir correctamente un string en caracteres. No con string.split(“”)

Dividir un string en caracteres parece una tarea sencilla, y lo es. mientras un carácter sea solo un carácter.

Como así?

En unicode existen caracteres que se combinan con otros, ejemplo de estos son los caracteres de acento. De esta forma a simple vista un carácter puede parecer un solo símbolo pero estar formado por dos o mas.

Por ejemplo la letra “a” con tilde puede ser un solo carácter (u+00e1), pero puede darse el caso que sea una letra a + una tilde (a+’) que son dos caracteres (u+0061 y u+0301 respectivamente).

Esto es un problema, en especial para una aplicación que necesite acceder a cada uno de los caracteres de una palabra como en los crucigramas, ahorcado, sopas de letras, etc.

Por ejemplo en mi juego de sopa de letras a veces las palabras en la cuadricula quedaban en la posición incorrecta. El problema, algunas palabras usaban mas espacio debido a los caracteres de combinación (eje. “mamá” ocupando 5 cuadros = m | a | m | a | ‘ |).

Que puede salir mal

Para conocer el largo de una cadena de texto normalmente usamos la propiedad length… error, ésta solo da resultados correctos si cada carácter en el texto esta formado por un solo codepoint.

alert("mamá".length)

Gracias a un enlace en MDN (Mozilla Developer Network) encontre esta solución.  para obtener el largo correcto del string contamos los caracteres que no son marcas de combinación. a continuación la función (con algunas modificaciones).

// regexp = rangos de caracteres correspondientes a marcas de combinación
rCombiningMarks = /[u0300-u036Fu1DC0-u1DFFu20D0-u20FFuFE20-uFE2Fu0483-u0489u0591-u05BD]/g;

function stringLength(string) {
// iniciar un contador de caracteres en 0
var length = 0;

// por cada caracter en string
for (var i = 0, l = string.length; i < l; i++) {
// si no es una marca de combinación
if (! rCombiningMarks.test(string.charAt(i)) )
// incrementar el contador de caracteres
length += 1
}

return length;
}

Probamos nuestra función.

alert(stringLength("mamá"));

Como dividir un string?

Podemos usar un método similar al anterior para dividir un string:
// regexp = rangos de caracteres correspondientes a marcas de combinación
rCombiningMarks = /[u0300-u036Fu1DC0-u1DFFu20D0-u20FFuFE20-uFE2Fu0483-u0489u0591-u05BD]/g;

function splitString(string) {
// iniciar un array de caracteres
var chars = [],
lastChar;

// por cada caracter en string
for (var i = 0, l = string.length; i < l; i++) {
// si no es una marca de cominación
if (! rCombiningMarks.test(string.charAt(i)) ) {
// Agregar al array de caracteres
chars.push(string.charAt(i));
}
// de lo contrario
else {
// Concatenar con ultimo caracter en el array
var lastChar = chars[chars.length - 1];
chars[chars.length - 1] = lastChar + string.charAt(i);
}
}

return chars;
}

// TEST
alert("con string.split: "+("mamá".split("").join(" | ")));
alert("con splitString: " + splitString("mama").join(" | "));

Conclusión

Aun cuando javascript nos provee varios métodos útiles para el manejo de strings, números, arrays y objetos, estos no siempre nos darán los resultados deseados. debemos probar nuestro código para estar seguros que se comporta de la forma correcta.

Algún otro método/función nativa de javascript de la que no hay que confiar. Déjalo en los comentarios.

Categories
Canvas html5 javascript tutorial

Canvas desde cero: primeros pasos

El elemento canvas es una gran herramienta para crear juego, animaciones y manipular imágenes, aun así no todos los navegadores soportan esta etiqueta, por tanto, es necesario contar con alternativas para no dejar de lado a los usuarios de dichos navegadores.

En esta primera parte veremos dos de estas alternativas así como algunas particularidades de la etiqueta canvas.

Compatibilidad

En la actualidad los únicos navegador que no cuentan con soporte para el elemento canvas son Internet Explorer 6, 7 y 8; no obstante existen soluciones (polyfills) que emulan su funcionalidad, entre los que se destacan:

  • excanvas: utiliza VML, una tecnología propietaria de Microsoft similar al svg y no necesita de plugins, pero carece de algunos métodos (getImageData, toDataURL…).
  • flashcanvas: utiliza flash (duh), es mas rápido que excanvas (a mi parecer) y soporta mas características, pero hay que comprar una licencia para poder desarrollar aplicaciones de comerciales.

La etiqueta canvas

El elemento canvas cuenta con una etiqueta de apertura y una de cierre, pero a diferencia de otras etiquetas, su contenido solo es visible en navegadores antiguos (ya que estos ignoran las etiquetas desconocidas) lo cual nos permite mostrar un contenido alternativo a los usuarios de dichos navegadores.

<canvas height="150" id="canvas" width="300">
<!-- contenido a mostrar si el navegador no soporta canvas -->
<img src="/images/imagen-estatica.jpg" />
</canvas>

Otro hecho a destacar es que sus medidas se declaran mediante los atributos width/height y no a través css. al usar css se escala el elemento, así, si el canvas mide 300×150 y cambiamos sus medidas por 600×300 los pixeles “medirán el doble” y la imagen se verá borrosa.

Por ejemplo, los siguientes cuadros se dibujaron en la mismas posición (10, 10) y tienen las mismas medidas (30×30) pero el canvas de la derecha fue escalado 6x usando css.

Note el borde borroso el la segunda imagen 

Detectar soporte

Antes de empezar a usar el canvas necesitamos saber si el navegador siquiera soporta dicho elemento, para esto:

  1. Creamos un elemento canvas y comprobamos si cuenta con el método getContext
  2. De lo contrario verificamos que exista un Polyfill
if (! document.createElement("canvas").getContext || ! window.G_vmlCanvasManager )
alert("Actualize su navegador");

Accediendo desde javascript

Para poder acceder al elemento desde javascript hay esperar que el DOM este listo, para esto puedes utilizar la función ready de jQuery o colocar el script justo antes de </body>.

A continuación obtenemos una referencia al contexto de dibujo del canvas utilizando el método getContext:

var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");

Ahora si podemos empezar a dibujar:

ctx.fillStyle = "red";
ctx.fillRect(50, 50, 30, 20);

Utiliza un navegador moderno para poder ver el ejemplo

TIPS

Antes de poder acceder al contexto del canvas en IE<9 (usando excanvas/FlashCanvas) debemos inicializarlo, para esto, en lugar de usar el método getContext podemos usar la siguiente función:

function getContext(canvas) {
if (! canvas.getContext && window.GV_ContextCanvas) {
G_vmlCanvasManager.init(canvas);
} else {
throw new Error("El navegador no soporta el elemento canvas");
}

return canvas.getContext("2d");
}

Conclusión

Finalizando esta primera parte tenemos todo listo para empezar a trabajar con el canvas. con suerte ahora podrás mostrar contenido alternativo para usuarios con navegadores antiguos, detectar el soporte del elemento canvas ya sea de forma nativa o mediante un polyfill y obtener el contexto de dibujo.

No te pierdas la siguiente parte en la que veremos el sistema de coordenadas y como dibujar formas básicas.

Si te gusto el tutorias compartelo en twitter o subscribete y te avisare cuando esté disponible la siguiente parte.

Categories
Canvas html5 javascript programación tutorial

Canvas desde cero

Si llevas tiempo con ganas de aprender a usar el canvas o quieres refrescar tus conocimientos,
no puedes perderte esta serie de tutoriales que arranca el día de hoy, donde no solo se explicara
como usar x función sino que desarrollaremos programas reales donde podrás aplicar lo aprendido.

y quien es usted para decirme que hacer

Soy un tipo al que le gusta la programación, en especial todo lo relacionado con la web (javascript *¬*)
y desde que conocí el elemento canvas quede fascinado con la idea de poder dibujar en navegador.
si bien no soy un experto y aun sigo aprendiendo, ya llevo tiempo trabajando con este elemento
y he aprendido mucho gracias a la comunidad y esta es mi forma de contribuir en algo.
algunos de los trabajos que he hecho:

P.D.: Si ven que me estoy colgando en la actualización del blog no duden en jalarme las orejas en twitter XD.

Categories
deshacer javascript redo rehacer tutorial undo

Rehacer – Deshacer en javascript

En este tutorial intentaré explicar la forma de implementar la funcionalidad de deshacer y rehacer, una característica muy usada en los programas de escritorio y que muchas aplicaciones web no tienen (y hace mucha falta ya que errar es de humanos).
Descargar código
La idea consiste en tener objetos que representen las acciones (mover, eliminar, resaltar…), los cuales deben guardar el estado anterior del objeto afectado.

ChangeColorCommand = function(obj, color) {

this.obj = obj;
this.new_color = color;

// guardar estado anterior
this.prev_color = obj.style.backgroundColor;
}

ChangeColorCommand.prototype.execute = function() {
this.obj.style.backgroundColor = this.new_color;
}

ChangeColorCommand.prototype.undo = function() {
this.obj.style.backgroundColor = this.prev_color;
}

Cada objeto cuenta con dos métodos

  • execute: ejecuta la acción (dah)
  • undo: deshace la acción valiéndose de los datos guardados previamente en el constructor.

Las acciones son administradas por un objeto que mantiene dos pilas de acciones: las que se pueden deshacer y las que se pueden rehacer:

  • cuando ejecuta una acción, la guarda en undo_stack y borra toda acción que se podía rehacer
  • cuando se deshace algo, saca el ultimo objeto de la pila undo_stack, ejecuta el método undo y guarda la acción en la pila redo_stack.
CommandManager = function(max_undo) {

// máxima cantidad de acciones guardadas
max_undo = max_undo || 30;

// pilas de acciones
this.undo_stack = [];
this.redo_stack = [];

// ejecutar comando cmd
this.executeCommand = function(cmd){
cmd.execute();

// si se sobrepasa cantidad de acciones
// eliminar primer elemento
if (this.undo_stack.length &amp;gt;= max_undo) {
this.undo_stack.shift();
}
this.undo_stack.push(cmd);
this.redo_stack = [];
}

// deshacer acción
this.undoCommand = function() {
var cmd = this.undo_stack.pop();

// si existe acción
if ( cmd ) {
cmd.undo();
this.redo_stack.push(cmd);
}
}
}

Para realizar una acción creamos una instancia del objeto apropiado y la pasamos al CommandManager (previamente creado).

var UndoRedo = new CommandManager(),

box = document.getElementById("box");

// cambiar color a rojo
UndoRedo.executeCommand(new ChangeColorCommand(box, "red"));

// cambiar color a verde
UndoRedo.executeCommand(new ChangeColorCommand(box, "green"));

// cambiar de verde a rojo
UndoRedo.undoCommand();

// cambiar de rojo a blanco
UndoRedo.undoCommand();

Ahora solo resta crear mas acciones (mover, negrita, itálica…) dependiendo de la aplicación que estemos desarrollando.

Eso es todo, espero les haya servido; si tienen alguna duda o critica no duden en dejarla en los comentarios.

Categories
Canvas event html5 javascript programación script

Beta 2 de canvas-event disponible

Ya esta disponible la beta 2 de canvas-event, esta revisión cuenta con algunas mejoras y corrección de errores como:

  1. Posibilidad de adicionar una función a varios eventos simultáneamente
  2. Las figuras dibujadas dentro de un manejador de eventos ahora si se visualizan
  3. Los Objetos Line ahora también pueden ser arrastrados
  4. Los bordes redondeados ahora también funcionan en Opera
Espero que les sea tan útil como lo ha sido para mi.
Categories
Canvas event html5 javascript jQuery software libre

canvas-event: fácil interacción con el elemento canvas

Uno de los elementos HTML5 que sin duda ofrece mayores posibilidades es el elemento canvas, gracias a este es posible crear y manipular imágenes directamente en el navegador, pero a la hora de crear algo mas que imágenes estáticas  nos tropezamos con la falta de métodos para el manejo de eventos, aun así encontramos aplicaciones web que permiten la interacción por medio del teclado y el ratón.

¿Como logran esta interacción?, si vemos en el código de estas aplicaciones, todas siguen mas o menos el mismo patrón:

  1. Agregar evento mousemove al elemento canvas
  2. comparar coordenadas del cursor con las de Objetos dibujados
  3. Seleccionar objeto sobre el cual se encuentra el cursor
  4. Al ocurrir un evento, ejecutarlo sobre el Objeto seleccionado
  5. Redibujar el canvas
No es algo difícil, pero podría ser mucho mas fácil.

Con esto en mente y luego de robar algunas ideas y algo de código de otros proyectos como procesingjs, jQuery, jquery-hotkeys y canto-js, nació canvas-event, una librería que se encarga del manejo de los eventos, facilitando el desarrollo de aplicaciones interactivas en el elemento canvas; ademas cuenta con objetos predefinidos como círculos, rectángulos, rutas e imágenes entre otros.

Algunos ejemplos de lo que se puede hacer con canvas-event:

Cv = Cevent("id-canvas");

// encadenamiento de métodos
Cv.circle( 40, 40, 40 )
.attr({fill: 'red'})
.rotate( 30 );

// manejo de eventos al estilo jquery
Cv.click( function(e) {
this.fill = this.fill == 'green' ? 'red' : 'green';
});

// eventos del teclado asociados a combinación de teclas
Cv.keydown( 'ctrl+s', function(e){
alert( "el Objeto ha sido guardados");
return false;
});

// Drag and drop
Cv.ellipse( 30, 40, 50, 10 )
.drag({
start: function() { console.log( "drag start" ); },

move: function() { console.log( "move to ", this.x, this.y ); },

end: function() { console.log( "drag end" ); }
})

// identificadores de Objetos
Cv.image(50, 100, "ball.gif").addId("pelota").rect(100, 50, 20).draw();

// Recuperar elementos por su Id o tipo;
Cv.find('#pelota').attr("alpha", .5).redraw();

Cv.find('rect').click(function(){
alert( "Hola, soy un rectangulo" );
});

// Eventos live
Cv.mouseover( "#pelota", function(e){
alert( "Soy una pelota" );
});

// Este Objeto también tendrá el evento anterior
Cv.circle(175, 75, 20).addId('pelota');

Ademas, podemos definir nuestras propias figuras extendiendo el objeto Shape:

var Triangle = Cevent.Shape.extend({
// inicializador
init: function( x, y, size ){
this.size = size || 5;
this._super( x, y );
},

// ctx es el context en el que se debe dibujar
draw: function( ctx ) {

ctx.save();

// aplicamos estilo y trasformaciones
this.applyStyle( ctx );
this.setTransform( ctx );

// definimos el contexto en el cual dibujaremos
// para poder usar todos los métodos svg definidos en Cevent
// como M (move) c (cubicBlazier) etc
Cevent.beginPath( ctx )
.M( this.x, this.y )
.h( 1 * this.size )
.v( 1 * this.size )
.z();

if ( this.fill ) { ctx.fill(); }

if ( this.stroke ) { ctx.stroke(); }

ctx.restore();
}
});

// registramos el nuevo objeto
Cevent.registre( "triangle", Triangle );

// ahora podemos hacer uso del nuevo objeto
var Cv = Cevent( "canvas-id" );

Cv.triangle( 40, 30, 10 )
.attr({
fill: "#050",
alpha: .5
})
.focus(function() {
this.fill = "#999";
})
.blur(function() {
this.fill = "#050";
})
.draw();

Por el momento falta documentación pero se incluyen varios ejemplos de su uso, si tienen alguna duda y/o sugerencia por favor déjenla en los comentarios o en la pagina del proyecto en google code.

Categories
html5 javascript jQuery password placeholder plugin

jquery.placehold: placeholders para inputs text y password

Aun cuando los navegadores modernos soportan el nuevo atributo placeholder (firefox 3.7+, safari 4.0+, chrome 4.0+), debemos seguir usando javascript para navegadores mas antiguos; por esto decidí buscar algún plugin para jQuery que me facilitara el trabajo, pero muy pocos soportaban inputs tipo password y los que lo hacían tenían comportamientos extraños en IE (como raro no creen), así que tome el mejor que encontré y le hice algunas modificaciones.

Su utilización es muy simple:

$("[placeholder]").placeholder( "myPlaceholderClass" );

Podemos pasar un nombre de clase por medio del cual aplicar estilo al placeholder (por defecto es placeholder o_O).

.myPlaceholderClass {
color: #999;
}

Pueden descargarlo del siguiente enlace, espero les sea útil.

Categories
chorradas javascript programación python script Wingdings

Revertir guardado en Wingdings

El otro día me encontraba aburrido a mas no poder, cuando de pronto me llaman a hacerme la siguiente pregunta: Imaginen que tienen un documento y no quiere que nadie mas se entere de lo que contiene, nada mejor que guardar dicho documento con el tipo de letra Wingdings, el problema es revertir el proceso, aunque no lo crean es algo mas común de lo que párese, la única solución es ir reemplazando cada signo por el caracter original, un proceso algo tedioso si se decide hacer a mano, así que porque no crear un script (en python) que automatice el proceso.

Para empezar tenemos que saber la correspondencia entre caracteres y signos, así que procedemos a abrir word e introducir los caracteres mas utilizados (letras mayúsculas y minúsculas, números, signos de puntuación, espacio) en una tipo de letra normal, con estos creamos una variable que llamaremos decode en nuestro código fuente, luego nuevamente en word cambiamos el tipo de letra por Wingdings y creamos otra variable que llamaremos encode, estas variables tienen que ser string unicode ya que los símbolos usados por la letra Wingdings son de lo mas raros; luego es cuestión de en un bucle ir reemplazando las coincidencias del caracter de encode en la posicion i con el carracter de decode en la misma posición. El script completo seria algo así:

#!/usr/bin/python
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# Por: Jhonatan sneyder Salguero Villa (sney2002@gmail.com)

import codecs
from os.path import split, splitext

def translate(path=""):
"""" Convertir texto en Wingdings a caracteres normales ""

text2decode = codecs.open(path, 'r', 'utf-8').read()

decode = u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!”?¿¡.,;():/*-+=&%$# @"
encode = u"”¿¡"

for i in range( len(decode) ):
text2decode = text2decode.replace( encode[i], decode[i] )

filename = splitext( split( path )[1] )[0]

file = codecs.open('resultado_%s.txt' % filename, 'wb', 'utf-8')

file.write( text2decode )

file.close()

if __name__ == "__main__":
import sys

try:
file = sys.argv[1]
translate( file )
except:
print "uso: translate /ruta/archivo"

Ahora solo hay que copiar en un editor de texto plano lo que desean convertir, guardarlo y ejecutar el script dándole la ruta al archivo. Si alguien se pregunta por que no utilice el método translate de string, la razón es que la función maketrans no acepta caracteres unicode.

El script tiene algunos defectos como el no manejar excepciones en caso que el archivo en path no exista, solo funciona con este tipo de letra y en cada iteración crear un nuevo string. Como siempre todas las criticas son bienvenidas.

Si no tienen instalado el interprete python, pueden usar este “codificador/decodificador” Wingdings XDDD, esta escrito en javascript así que si usan demasiado texto puede bloquearse.

Categories
closure javascript programación y combinator

Entendiendo la función Y combinatoria

Estando un poco aburrido y sin nada que hacer, se me vino a la mente una extraña pieza de código que había visto por ahí, esta era la función Y combinatoria. donde la vi o porque de repente pensé en ella no importa, lo importante era que ya no me la podía sacar de la mente, era como cuando se escucha mucho una canción y luego sin pensarlo terminas repitiéndola una y otra vez en la mente; así que hice lo único que podía hacer, partirme el coco intentando entender que era lo que hacia y como lo hacia. Empece como siempre preguntándole al omnisciente Google, gracias al cual encontré artículos muy interesantes, pero el que me pareció mas interesante fue uno donde a modo de ejercicio mental se va obteniendo la función Y a partir del ya conocido factorial, pero todo iba bien hasta que como mas o menos a la mitad de la explicación me perdí, así que decidí realizar el mismo ejercicio pero a mi propia manera; el resultado de ese ejercicio es este post que me salio mas largo de lo que pensé; alguien puede argumentar que es una vil copia, pero me vale. Ahora si después de esta pequeña introducción manos a la obra. A continuación la ya mencionada función Y para que vayan viendo de lo que estoy hablando:

Antes de empezar debemos tener claro lo que es closure, hay muchos artículos en los que hablan sobre este tema, pero aquí daré una explicación a mi forma de entender. una closure (no encuentro una forma adecuada de traducirla al español) es la “propiedad” que tienen las funciones anidadas de tener acceso a las variable definida en la función en la cual están contenidas (siempre y cuando no la sobrescriban) aun después de que esta ultima sea “destruida”. Como sabemos todas la variables definidas dentro de una función (por medio de var) son locales a esta y al terminar su ejecución son “destruidas”. a continuación un ejemplo:

function f(){
var x = 2;
alert( 'x vale ' + x );
};

f();// alerta 'x = 2'

alert(x):// Error variable no definida

Como vemos la variable x solo existía en el interior f. Ahora viene lo divertido, ya que si creamos una función anidada y la retornamos para hacerla visible al exterior, esta seguirá teniendo acceso a x aun después de finalizada la ejecución de f:

function f(){
var x = 2;
alert( 'x = ' + x );
return function(n){
x += n;
alert('ahora x vale ' + x );
}
};

fclosure = f(); // alerta 'x vale 2'

alert('x vale '+ x):// Error x no esta definida fuera de f

/* fclosure aun tiene acceso a x */
fclosure(1);// alerta 'ahora x vale 3'

Creo que no hacen falta mas explicaciones, esto es closure; ya que quedo claro este concepto (eso creo) podemos continuar. Ahora si empezaremos con la función factorial he iremos avanzando hasta llegar paulatinamente a la definición de Y, como ya saben la función factorial luce algo así:

function factorial(n) {
return n < 2 ? 1 : n * factorial(n-1);
}

Como vemos la función factorial se llama a si misma usando su propio nombre, este es precisamente el “problema” que resuelve Y al permitir la recursion anónima en lenguajes donde no esta soportada nativamente. como una anotación aparte dejemos en claro que javascript permite la recursion anónima gracias a la propiedad callee del objeto arguments que esta disponible en el interior de todas las funciones como vemos a continuación:

// arguments.callee es una referencia a la propia funcion
(function(n){ return n<2 ? 1 : n * arguments.callee(n-1)})(4) // retorna 24

De esta forma definimos una función que se llama a si misma sin la necesidad de usar su propio nombre (lo cual seria imposible ya que no posee uno). una vez aclarado esto empecemos por eliminar la necesidad de crear recursion en la función factorial por medio de su nombre, esto se puede lograr creando una función que llamaremos curry la cual toma como argumento una función f que en este caso sera ella misma, curry retornara la función #1 (factorial) que gracias a una closure creada (suena feo) podrá acceder a f para iniciar la recursion:

curry = function(f) {
return function(n) { // #1
return n < 2 ? 1 : n * f(f)(n-1);// llamamos la función sin usar su nombre
};
};
curry(curry)(4); // pasamos como argumento la propia función

Con esto solucionamos el problema de utilizar el nombre de la función para crear la recursion, pero la parte f(f)(n-1) se ve algo antinatural, seria mejor algo como f(n-1); si pensamos bien, la solución mas lógica es crear otra función anidada (#2) dentro de #1 la cual tome como argumento el resultado de f(f), se vería así:

curry = function(f) {
return function(n) { // #1
function g(h, n) { // #2 toma el resultado de f(f) en h
return n < 2 ? 1 : n * h(n-1); // h == f(f)
};
return g(f(f), n); // pasamos como parámetro a f(f) y n
};
};

curry(curry)(4); // retorna 24

Podríamos decir que hemos terminado, pero el tener que llamar a factorial (#2) con un argumento de mas (h) no parece algo lógico, así que separamos estos argumentos gracias a una tercera función anidada:

curry = function(f) {
return function(n) { // #1
function g(h) { // #2 toma el resultado de f(f) en h
return function (n) { // #3 solo toma n
return n < 2 ? 1 : n * h(n-1); // h == f(f);
};
};
return g(f(f))(n); // ejecutamos #2
};
};

curry(curry)(4); // retorna 24

Si nos fijamos bien la función #2 (factorial) podría ser cualquier otra (eje. fibonacci), lo realmente importante es donde se ejecuta, para lo cual lo único necesario es tener acceso a ella ya sea por estar definida globalmente o por closure, como se desaconseja el uso de variables globales lo haremos de la segunda forma tomando la función curry y anidandola en otra función que llamaremos Y :), que recibirá como argumento alguna función f para que #2 pueda acceder a ella por closure y así poder ejecutarla, también cambiamos la forma de llamar a f y usaremos apply para poder usar funciones que reciban mas de un argumento:

function Y(f){ // f = cualquier función con el formato de #factorial
function curry(g) { // g == curry
return function() { // #1
return f(g(g)).apply(null, arguments);
};
};
return curry(curry); // ejecutamos curry
};

/* ¡funciona! */
fac = Y(function(f){ // f = funcion
return function(n) { // retornar una función
return n < 2 ? 1 : n * f(n-1); // acceder a f por closure
};
});

fac(4); // retorna 24

y así es como funciona Y, aunque generalmente se encuentra escrita de la siguiente forma:

function Y(f){
return (function(h) { // h = curry
return h(h); // ejecutamos curry
}(function (g) { // la que llame curry
return function() { // #1
return f(g(g)).apply(null, arguments);
};
}));
};

Pues eso era todo, aunque casi se me estalla un aneurisma, por fin entendí como funciona la bendita función Y. espero que a alguien mas le haya sido de utilidad este ejercicio y perdonaran los errores ortográficos y/o de redacción pero esto de la escritura no me va de a mucho.