miércoles, 28 de diciembre de 2016

setTimeout y setInterval con argumentos en la función de llamada y posibilidad de aplicar a objetos

(1) setTimeout:
//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.5+
//              JScript    5.5+
// Environments NN6+ IE5.5+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------

function setTimeoutEx 
(
      f // src 
    , m // milliseconds
    , o /* target
    , arg1 [, ... [, argN]] */
){
    var i = typeof f;

    if (i == 'string' || i == 'function')
    {
        if (!setTimeoutEx.__cnt)
            setTimeoutEx.__cnt = 1;

        if (i == 'string')
            f = new Function(f);

        if (m == null) 
            m = 0;

        setTimeoutEx[i = setTimeoutEx.__cnt++] = 
        {
              a: Array.prototype.slice.call(arguments, 3)
            , o: o
            , f: f
            , i: setTimeout
                 (
                      'setTimeoutEx[' + i + '].f.apply(setTimeoutEx[' + 
                      i + '].o, setTimeoutEx[' + i + '].a)'
                     , m
                 )
        };

        return setTimeoutEx[i].i;
    }

    return -1;
}

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

function clearTimeoutEx (i /* id */)
{
    if (typeof i == 'number' && i > 0)
    {
        clearTimeout(i);

        for (var j = 0; j < setTimeoutEx.__cnt; ++j)
            if (setTimeoutEx[j] && setTimeoutEx[j].i == i) 
                delete setTimeoutEx[j];
    }
}
(1) Test:
setTimeoutEx
(
      function (s)
      {
          alert(s);
      }
    , 1000
    , null
    , '¡Test!'
);

var myobj = {test: 'Ok!'};

setTimeoutEx
(
      function (s)
      {
          alert(this.test);
          this.arg1 = s;
      }
    , 1000
    , myobj
    , 'my arg test ok!'
);
(2) setInterval:
//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.5+
//              JScript    5.5+
// Environments NN6+ IE5.5+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------

function setIntervalEx 
(
      f // src 
    , m // milliseconds
    , o /* target
    , arg1 [, ... [, argN]] */
){
    var i = typeof f;
     
    if (i == 'string' || i == 'function')
    {
        if (!setIntervalEx.__cnt)
            setIntervalEx.__cnt = 1;

        if (i == 'string')
            f = new Function(f);

        if (m == null) 
            m = 0;

        setIntervalEx[i = setIntervalEx.__cnt++] = 
        {
              a: Array.prototype.slice.call(arguments, 3)
            , o: o
            , f: f
            , i: setInterval
                 (
                      'if (setIntervalEx[' + i + '].f.apply(setIntervalEx['   + 
                      i + '].o, setIntervalEx[' + i + '].a))clearIntervalEx(' +
                      'setIntervalEx[' + i + '].i)'
                     , m
                 )
        };

        return setIntervalEx[i].i;
    }

    return -1;
}

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

function clearIntervalEx (i /* id */)
{
    if (typeof i == 'number' && i > 0)
    {
        clearInterval(i);

        for (var j = 0; j < clearIntervalEx.__cnt; ++j)
            if (clearIntervalEx[j] && clearIntervalEx[j].i == i) 
                delete clearIntervalEx[j];
    }
}
(2) Test:
setIntervalEx
(
      function (s, b)
      {
          alert(s);
          return b;
      }
    , 1000
    , null
    , '¡Test!'
    , true
);

var myobj = {test: 'Ok!'};

setIntervalEx
(
      function (s)
      {
          alert(this.test);
          this.arg1 = s;

          return true;
      }
    , 1000
    , myobj
    , 'my arg test ok!'
);

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);