miércoles, 10 de julio de 2013

Codificación de base 2 a base 63 en JavaScript

El método toString sólo admite hasta la base 36, así que hay que partir de una nueva codificación. Base32 no tiene la especificación rfc4648, pero con el presente objeto se pueden codificar todos los caracteres desde la base 2 a la 63 (aunque podrían ser más):
//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.3+
//              JScript    5.5+
// Environments NN4.06+ IE5.5+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
var Coding =
{
      Num:
      {
            codec : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
          , getBlockSize : function (b /* radix [2..63] */)
            {
                if      (b == 2)          b = 8;
                else if (b == 3)          b = 6;
                else if (b > 3 && b < 7)  b = 4;
                else if (b > 6 && b < 16) b = 3;
                else                      b = 2;
 
                return b;  
            }

            //------------------------------------------------------------------------------------

          , encode: function (n /* number */, b /* radix [2..63] */)
            {
                var s = '';
                if (!isNaN(n) && !isNaN(b)) 
                {
                    if (typeof n == 'string')
                        n = parseInt(n);
 
                    b = Math.min(Math.max(2, b), 63);
       
                    do
                    {
                        s = this.codec.charAt(n % b) + s;
                        n = Math.floor(n / b)
                    }
 
                    while (n > 0);
     
                    n = Coding.Num.getBlockSize(b);
                    b = s.length;
     
                    while (b++ < n)
                        s = '0' + s;
                }

                return s;
            }

            //------------------------------------------------------------------------------------

          , decode: function (s /* src */, b /* radix [2..63] */)
            {
                var n = 0;
                if (s != null && !isNaN(b))
                {
                    b = Math.min(Math.max(2, b), 63);
        
                    for (var i = 0, l = (s += '').length; i < l; ++i)
                        n = n * b + this.codec.indexOf(s.charAt(i));
                }

                return n;
            }
      }

      //-----------------------------------------------------------------------------------------

    , Base2x63:
      {
            encode: function (s /* src */, b /* base [2..63] */)
            {
                var r = '';
                if (s != null && !isNaN(b))
                {
                    b = Math.min(Math.max(b, 2), 63);

                    for (var i = 0, l = (s += '').length; i < l; ++i)
                        r += Coding.Num.encode(s.charCodeAt(i), b);
                }

                return r;
            }

            //------------------------------------------------------------------------------------

         , decode: function (s /* src */, b /* base [2..63] */)
           {
                var n, r = '';
                if (s != null && !isNaN(b))
                {
                    b = Math.min(Math.max(b, 2), 63);
                    n = Coding.Num.getBlockSize(b);
     
                    for (var i = 0, l = (s += '').length; i < l; i += n)
                        r += String.fromCharCode(Coding.Num.decode(s.substr(i, n), b));
                }

                return r;
           }
      }
};
Test:
var e, s = 'Tomaré una decisión con la cigüeña.';
for (var i = 2; i < 64; ++i)
{
    e = Coding.Base2x63.encode(s, i);
    alert
    (
        'base: ' + i + ', length: ' + e.length + ', encoded: ' + e + ', decoded: ' + 
        Coding.Base2x63.decode(e, i)
    );
}

jueves, 4 de julio de 2013

Test Criptográfico: PHP mcrypt y CryptoJS (AES)

<script type="text/javascript" src="cryptojs/3.1.2/rollups/aes.js"></script>
<script type="text/javascript" src="cryptojs/3.1.2/components/pad-zeropadding.js"></script>
<form method="post" enctype="application/x-www-form-urlencoded" action="crypto.php">
    <input type="text" id="md5" name="md5" size="100" maxlength="100" />
    <input type="text" id="code" name="code" size="100" maxlength="100" />
    <input type="submit" value="Test" />
</form>
var hash = CryptoJS.MD5('8765432187654321');
var key  = CryptoJS.enc.Utf8.parse(hash);
var iv   = CryptoJS.enc.Utf8.parse('1234567812345678');

document.getElementById('md5').value  = hash;
document.getElementById('code').value = CryptoJS.AES.encrypt
(
      'Test Ok!'
    , key
    , {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding }
);
<?php

$text      = "Test Ok!";
$key       = md5("8765432187654321");
$iv        = utf8_encode("1234567812345678");
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
//$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($_POST["code"]), MCRYPT_MODE_CBC, $iv);

echo "md5: " . $_POST["md5"] . " -> " . $key . ", encrypted: " . $_POST["code"] . " -> " . base64_encode($encrypted) . ", decrypted: " . $decrypted; 

?>

miércoles, 3 de julio de 2013

Subir archivos con Ajax

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.8.5+
//              JScript    10+
// Environments IE10+ FIREFOX4+ CHROME6+ OPERA12+ SAFARI5+
//------------------------------------------------------------------------------------------------
function upload 
(
      s // action-url
    , e // input file (id | element)
    , u // user
    , p // password
){
    if (typeof e != 'object')
        e = document.getElementById(e);
 
    var f, l = 0;

    if ((f = e.files) && (l = f.length) == 1) // HTML5! (FileList)
    {
        var h = new XMLHttpRequest();
  
        h.open('POST', s, false, u, p);
        h.setRequestHeader('Content-type', 'multipart/form-data');
        h.setRequestHeader('X-File-Name', (f = f[0]).name);
        h.setRequestHeader('X-File-Size', f.size);
        h.setRequestHeader('X-File-Type', f.type);
        h.send(f);
  
        return h.readyState == 4 && h.status == 200;
    }
 
    else if (l > 1)
    {
        f = [];
        for (var i = 0; i < l; ++i) f.push
        ({
               fileName : e.files[i].name
             , uploaded : upload(s, {files: [e.files[i]]}, u, p)
        });
  
        return f;
    }
 
    return false;
}

Uso con un sólo archivo:

<input id="file" type="file" />
if (upload('upload.php', 'file'))
{
    //TODO...
}

Uso con múltiples archivos:

<input id="files" type="file" multiple />
var r = upload('upload.php', 'files');

for (var i = 0, l = r.length; i < l; ++i) if (!r[i].uploaded) 
{
    alert('"' + r[i].fileName + '" Not uploaded correctly!');
    // TODO...
}


upload.php:
<?php

$hd   = getallheaders();
$name = $hd["X-File-Name"];

if (strlen($name) > 0)
{
    //$size = $hd["X-File-Size"];
    //$type = $hd["X-File-Type"];
    // TODO...

    $in  = fopen("php://input", "r");
    $out = fopen("upload/" . $name, "w");
    while($data = fread($in, 1024)) fwrite($out, $data);
    fclose($out);
    fclose($in);
}

?>
upload.jsp:
<%@ page import="java.io.*" %>
<%
    String fileName = request.getHeader("X-File-Name");
    if (fileName != null && fileName.length() > 0)
    {
        //String fileSize = request.getHeader("X-File-Size"); 
        //String fileType = request.getHeader("X-File-Type");    
        //TODO..
    
        FileOutputStream fos    = new FileOutputStream("webapps/test/upload/" + fileName);
        InputStream      is     = request.getInputStream();
        byte[]           buffer = new byte[1024];
        int              length = 0;
    
        while ((length = is.read(buffer)) != -1)
            fos.write(buffer, 0, length);
  
        fos.flush();
        fos.close();
    }
%>
upload.aspx:
<%@ Page Language="C#" %>
<%
if (Request.ContentLength > 0)
{
    string fileName = Request.Headers["X-File-Name"];

    if (fileName != null)
    {
        //string fileSize = Request.Headers["X-File-Size"];
        //string fileType = Request.Headers["X-File-Type"];
        //TODO...
         
        Request.SaveAs(Server.MapPath("/upload/") + fileName, false);
    }
}
%>
upload.asp:
<%@ Language="VBScript" %>
<%
Function Save (fileName, byteArray)
    on error resume next
    Dim  BinaryStream

    Set BinaryStream = CreateObject("ADODB.Stream")
   
    BinaryStream.Type = 1

    BinaryStream.Open
    BinaryStream.Write byteArray

    BinaryStream.SaveToFile Server.MapPath("upload") & "\" & fileName, 2
End Function

Dim strFileName
strFileName = Request.ServerVariables("HTTP_X-File-Name")

If (Len(strfileName) > 0) Then

    '... Request.ServerVariables("HTTP_X-File-Size")
    '... Request.ServerVariables("HTTP_X-File-Type")
    'TODO...

    Save strFileName, Request.BinaryRead(Request.TotalBytes)
End If
%>

martes, 2 de julio de 2013

Soporte "call" y "apply" para navegadores antiguos

Call:

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.1+
//              JScript    2+
// Environments NN3+ IE4+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
function __fn_call (o /* object, arg1 [, ... [, argN]] */)
{
    var a = arguments;
    var s = '';
    var u;

    for (var i = 1, l = a.length, n = l - 1; i < l; ++i)
    {
        s += 'a[' + i + ']';
  
        if (i < n) 
            s+= ',';
    }
 
    if (o == null)
        s = eval('this(' + s + ')');
  
    else
    {
        o.__callback = this;
        s = eval('o.__callback(' + s + ')');
        o.__callback = u;
    }
 
    return s;
}

//------------------------------------------------------------------------------------------------

if (!Function.prototype.call) Function.prototype.call = __fn_call;

Apply:

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.1+
//              JScript    2+
// Environments NN3+ IE4+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
function __fn_apply (o /* object */, a /* [args] */ )
{
    var u, s = '';
    if (a && a.constructor == Array) for (var i = 0, l = a.length, n = l - 1; i < l; ++i)
    {
        s += 'a[' + i + ']';
  
        if (i < n)
            s+= ',';
    }
 
    if (o == null)
        s = eval('this(' + s + ')');
  
    else
    {
        o.__callback = this;
        s = eval('o.__callback(' + s + ')');
        o.__callback = u;
    }
 
    return s;
}

//------------------------------------------------------------------------------------------------

if (!Function.prototype.apply) Function.prototype.apply = __fn_apply;

lunes, 1 de julio de 2013

Diálogos modales en JavaScript

Muestra un cuadro de diálogo modal con los parámetros especificados:
//------------------------------------------------------------------------------------------------
// Engines      JavaScript   1.4+
//              JScript      5.5+
// Environments NN6+ IE5.5+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
function getModalDlg
(
       s // url
     , c // callback
     , w // width
     , h // height
     , x // left
     , y // top
){
     if (s == null || (s += '').length < 1 || !/^((ht|f)tp(s)*\:\/\/)*\S+$/.test(s))
         throw new Error('getModalDlg: Invalid Url! "' + s + '"');

     if (typeof c != 'function')
         throw new Error('getModalDlg: Invalid callback function!');

     var a = Array.prototype.slice.call(arguments, 2);
     var b = a.length < 3;
     var f = ['width', 'height', 'left', 'top'];
 
     w = window;

     var p = w.showModalDialog;
     var d = p ? [':', 'px;'] : ['=', ','];
     var u = 'undefined';
     var m = typeof netscape != u;
     var n = navigator.userAgent;

     for (var i = 0; i < a.length; ++i)
         a[i] = (p ? 'dialog' : '') + f[i] + d[0] + a[i] + d[1];

     f = a.join('');

     if (p)
     {
         f += 'center:' + (b ? '1' : '0') + ';scroll:1;';
         f += n.indexOf('MSIE') + 1 ? 'status:0' : 'resizable:0';
         s  = p(s, null, f);
         
         c(typeof s.returnValue != u ? s.returnValue : s);
     }

     else
     {
         w.dlgcallback = c;
         f += 'dialog,scrollbars,chrome,modal=yes,location=0,status=0,toolbar=no';

         if (b) 
             f += ',centerscreen';

         if (m && (m = netscape.security.PrivilegeManager))
         { 
             try
             {
                 m.enablePrivilege(d = 'UniversalBrowserWrite');
                 w.open(s, '_blank', f);
                 m.disablePrivilege(d);

                 return;
             }

             catch (e) {}
         }

         if (typeof w.__dlg == u || w.__dlg.closed)
         {
             w.__dlg  = w.open(s, '_blank', f);

             try
             {
                 w.onfocus = function ()
                 {
                     if (typeof this.__dlg != u && !this.__dlg.closed)
                     {
                         this.blur();
                         this.__dlg.focus();
                     }

                     else delete this.onfocus;
                 }
 
                 w.__dlg.onblur = function ()
                 {
                     this.opener.blur();
                     this.focus();
                 }
             }

             catch (e)
             {
                 c    = { callback: c, dialog: w.__dlg};
                 c.id = setInterval
                 (
                       function (o)
                       {
                           var s = 'returnValue';
                           if (typeof o.dialog[s] != 'undefined')
                           {
                               o.callback(o.dialog[s]);
                               clearInterval(o.id);
                           }
                      }
                    , 0
                    , c
                );
             }
         }

         else if (typeof w.__dlg != u)
             w.__dlg.focus();
     }
}
Uso:
getModalDlg('my-url', function (returnValue) { /* TODO... */ }, 320, 240);
Código a insertar en la página del cuadro de diálogo:
function setDlgReturnValue (v /* value */)
{
     var s = 'returnValue';
     var w = window;

     if (w.showModalDialog || !w.opener || !w.opener.dlgcallback)
         w[s] = v;

     else w.opener.dlgcallback(v);
}
Uso:
setDlgReturnValue('my-value');
window.close();

viernes, 28 de junio de 2013

Inclusiones dinámicas de scripts en JavaScript

Verifica si ya se incluyó un script (en la sección de cabecera por defecto):

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.2+
//              JScript    3+
// Environments NN6+ IE5+ MOZILLA1+ SAFARI1+
// DOM Level    1 Core
//------------------------------------------------------------------------------------------------
function included (s /* scr */, t /* tag-name (head by default) */)
{
    var d;
    if (!t) t = 'head';

    if (typeof s == 'string' && (d = s.indexOf('../')) != -1)
        s = s.substr(d + 3);

    d = document;
    t = d.getElementsByTagName(t);

    for (var i = 0, l = t.length, e; i < l; ++i)
    {
        e = t[i].getElementsByTagName('script');

        for (var j = 0, n = e.length; j < n; ++j)
            if (e[j].src && (s.test ? s.test(e[j].src) : e[j].src.lastIndexOf(s) != -1))
                return true;
    }

    return false;
}

Uso:

if (!included('my-file.js'))
{
    // TODO...
}

Incluye uno o varios scripts (en la sección de cabecera por defecto):

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.2+
//              JScript    3+
// Environments NN6+ IE5+ MOZILLA1+ SAFARI1+
// DOM Level    1 Core
//------------------------------------------------------------------------------------------------
function include (/* arg1 [, ... [, argN]] */)
{
    var r = '', x = r;
    for (var d = document, i = 0, a = arguments, l = a.length, e, s; i < l; ++i)
    {
        if ((s = a[i]).root && (r = s.root + '').length > 0 && r.lastIndexOf('/') != r.length - 1)
            r += '/';
   
        if (s.extension && (x = s.extension + '').length > 0 && x.indexOf('.') != 0)
            x = '.' + x;

        if (s.src || typeof s == 'string')
        {
            e = d.createElement('script');
            e.setAttribute('src',  r + (s.src ? s.src : s) + x);
            e.setAttribute('type', s.type ? s.type : 'text/javascript');

            if (typeof s.onload == 'function')
            {
                if (e.readyState) 
                {
                    e.onreadystatechange = function ()
                    {
                        if (this.readyState == 'loaded' || this.readyState == 'complete')
                        {
                            delete this.onreadystatechange;
                            this.loaded();
                        }
                    }

                    e.loaded = s.onload;
                }
    
                else e.onload = s.onload;
            }

            d.getElementsByTagName(s.tag ? s.tag : 'head')[0].appendChild(e);
        }
    }
}

Uso:

if (!included('my-file1.js') && !included('my-file2.js')) include
(
      {root: 'my-path', extension: 'js'}
    , {src: 'my-file1', onload: function () { /* TODO... */ }}
    , {src: 'my-file2', onload: function () { /* TODO... */ }}
);

if (!included('my-file4.js'))
    include({src: 'my-file4.js', onload: function () { /* TODO... */ }});

Existe otro modo de hacerlo, usando ajax:

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.2+
//              JScript    3+
// Environments NN7.1+ IE5+ MOZILLA1+ SAFARI1.2+
//------------------------------------------------------------------------------------------------
function loadscript (s /* url */, b /* eval? (Boolean) */)
{
    var r, x, w = window;
 
    if (w && w.XMLHttpRequest)
        r = new w.XMLHttpRequest();
 
    else if (typeof Components != 'undefined' && Components.classes)                               // XPCOM!
    {
        try
        {
            r = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1'].createInstance
            (
                Components.interfaces.nsIXMLHttpRequest
            );
        }
   
        catch (e)
        {
            r = null;
        }
    }
 
    else if ((x = w.ActiveXObject) || (x = w.GeckoActiveXObject)) 
    {
        try
        {
            r = new x('Msxml2.XMLHTTP');
        }
  
        catch (e)
        {
            try
            {
                r = new x('Microsoft.XMLHTTP'); 
            }

            catch(e)
            {
                r = null;
            }
        }
    }
 
    if (r)
    {
        r.open('GET', s, false);
        r.send(null);
  
        if (r.readyState == 4 && r.status == 200)
        {
            if (b) eval(r.responseText);
            else
            {
                (w = (x = document).createElement('script')).setAttribute('type', 'text/javascript');
                w.text = r.responseText;
                x.getElementsByTagName('head')[0].appendChild(w);
            }

            return true;
        }
    }
    
    return false;
}

Uso:

if (loadscript('my-file1.js'))
{
    //TODO...
}

loadscript('my-file2.js', true);

Macros de C en JavaScript

//-----------------------------------------------------------------------------------------------
// Engines       JavaScript 1+
//               JScript    1+
// Environments  NN2+ IE3+ WSH1+ MOZILLA1+ SAFARI1+
//-----------------------------------------------------------------------------------------------

function makeWORD (l, h) { return  l & 0xFF | (h & 0xFF) << 8;                                  } // 0-255 * 2
function loBYTE   (n)    { return  n & 0xFF;                                                    }
function hiBYTE   (n)    { return (n >> 8) & 0xFF;                                              }

//-----------------------------------------------------------------------------------------------

function make3BYTE (a, b, c)    { return makeWORD(a, b) | (c & 0xFF) << 16;                     } // 0-255 * 3
function RGB       (r, g, b)    { return make3BYTE(r, g, b);                                    }
function makeQBYTE (a, b, c, d) { return make3BYTE(a, b, c) | (d & 0xFF) << 24;                 } // 0-255 * 4
function RGBA      (r, g, b, a) { return makeQBYTE(r, g, b, a);                                 }
function h3BYTE    (n)          { return (n >> 16) & 0xFF;                                      }
function h4BYTE    (n)          { return (n >> 24) & 0xFF;                                      }
function getRValue (n)          { return loBYTE(n);                                             }
function getGValue (n)          { return hiBYTE(n);                                             }
function getBValue (n)          { return h3BYTE(n);                                             }
function getAValue (n)          { return h4BYTE(n);                                             }

//-----------------------------------------------------------------------------------------------

function makeLONG (l, h)   { return l & 0xFFFF | (h & 0xFFFF) << 16;                            } // 0-65535 * 2
function loWORD   (n)      { return  n & 0xFFFF;                                                }
function hiWORD   (n)      { return (n >> 16) & 0xFFFF;                                         }

jueves, 27 de junio de 2013

Cómo obtener la versión de JavaScript y JScript

//--------------------------------------------------------------------------------------------------
// Engines      JavaScript   1+
//              JScript      1+
// Environments NN2+ IE3+ WSH1+ MOZILLA1+ SAFARI1+
//--------------------------------------------------------------------------------------------------

// Global
//--------------------------------------------------------------------------------------------------

function  __this() { return this; }
var __g = __this();

//--------------------------------------------------------------------------------------------------

var __err_t = __g.Error;
var __num_t = __g.Number;
var __rex_t = __g.RegExp;

// JScript
//--------------------------------------------------------------------------------------------------

var  __jsm_; /*@cc_on __jsm_ = @_jscript_version; @cc_off @*/       
if (!__jsm_)
     __jsm_ = __err_t ? 5 : (__rex_t ? 3 : (__num_t.MAX_VALUE ? 2 : 1));

// JavaScript
//--------------------------------------------------------------------------------------------------

var __jsn_ = __err_t ? 1.5 : (__g.Infinity ? 1.3 : (__rex_t ? 1.2 : (__num_t.MAX_VALUE ? 1.1 : 1))); 

if (__jsn_ == 1.5)
    __jsn_ = eval('[]').reduce ? 1.8 : (__g.Iterator ? 1.7 : (eval('[]').map ? 1.6 : 1.5));