miércoles, 31 de octubre de 2012

JavaScript: Copiar y Resaltar (Copy & Highlight)

Como parte de mis disciplina de documentar funcionalidades que me sacan canas, esta vez dejo a disposición un pequeño código que ejemplifica como hacer un copiado/pegado de un elemento del DOM y a su vez dejarlo seleccionado/resaltado.

Para el copy/paste utilizo una librería popular en la web que utiliza un archivo Flash como intermediario de la operación: ZeroClipboard. Para la selección encontré un código que funciona adecuadamente en los principales browsers (IE, FF y Chrome). En el código de abajo son las funciones "getTextNodesIn" y "setSelectionRange". Cuando se carga este código en el browser, el usuario puede hacer clic en el texto y automáticamente se copia mostrando a su vez un mensaje de alerta, para después dejarlo seleccionado.

<html>
<head>
<script type="text/javascript" src="ZeroClipboard.min.js"></script>  
<script type="text/javascript">
 function getTextNodesIn(node) {
  var textNodes = [];
  if (node.nodeType == 3) {
   textNodes.push(node);
  } else {
   var children = node.childNodes;
   for (var i = 0, len = children.length; i < len; ++i) {
    textNodes.push.apply(textNodes, getTextNodesIn(children[i]));
   }
  }
  return textNodes;
 }

 function setSelectionRange(el, start, end) {
  if (document.createRange && window.getSelection) {
   var range = document.createRange();
   range.selectNodeContents(el);
   var textNodes = getTextNodesIn(el);
   var foundStart = false;
   var charCount = 0, endCharCount;

   for (var i = 0, textNode; textNode = textNodes[i++]; ) {
    endCharCount = charCount + textNode.length;
    if (!foundStart && start >= charCount
      && (start < endCharCount ||
      (start == endCharCount && i < textNodes.length))) {
     range.setStart(textNode, start - charCount);
     foundStart = true;
    }
    if (foundStart && end <= endCharCount) {
     range.setEnd(textNode, end - charCount);
     break;
    }
    charCount = endCharCount;
   }

   var sel = window.getSelection();
   sel.removeAllRanges();
   sel.addRange(range);
  } else if (document.selection && document.body.createTextRange) {
   var textRange = document.body.createTextRange();
   textRange.moveToElementText(el);
   textRange.collapse(true);
   textRange.moveEnd("character", end);
   textRange.moveStart("character", start);
   textRange.select();
  }
 }

 function highlight() {
  var element = document.getElementById("span_id");
  setSelectionRange(element, 0, element.innerHTML.length); 
  alert("copied!");
 }

</script>
</head>
<body>
 <span id="span_id">Copy Me!</span>
 <script>
  ZeroClipboard.setMoviePath( 'ZeroClipboard.swf' );
  var clip = new ZeroClipboard.Client();
  var element = document.getElementById("span_id");
  clip.setText( element.innerHTML )
  clip.glue( 'span_id' ); 
  clip.addEventListener( 'onMouseUp', highlight );
 </script>
</html>
</body>


lunes, 29 de octubre de 2012

Hacer que un elemento DIV se comporte como Link

Si se desea que un elemento DIV pueda ser "cliqueable" en toda su área de manera que se comporte como una ancla (etiqueta "A"), simplemente se cambia el estilo para que el cursor sea "pointer", y se agrega el evento de onclick:

<div style="cursor:pointer;" onclick="window.location='new_page.html'>
...
</div>

miércoles, 10 de octubre de 2012

DOS: Listar todos los archivos de un tipo de manera recursiva

Hace poco quería listar de manera recursiva todos los .html que tenía en una carpeta. Pensé que tenía que crear un script de Windows pero resulta que con un simple comando de DOS resulta suficiente:

dir /s/b *.html > results.txt

domingo, 7 de octubre de 2012

Android: Nombres de recursos

Si obtienes un error diciendo lo siguiente:

"Invalid file name: must contain only [a-z0-9_.]"

Es porque alguno de los recursos que colocaste en la carpeta "res" no sigue las reglas de nombres de archivos apropiada.

Recientemente, en mis primeras incursiones en el desarrollo Android, nombre un archivo como "the-beatles-band". Según yo estaba usando caracteres válidos, pero no había notado que el guion que sale en la regla es para describir una expresión regular. "a-z" significa letras de la "a" a la "z" en minúscula. De igual forma "0-9" significa los números del 0 al 9. Así que tuve que reemplazar el guion por la raya abajo ("_").

miércoles, 3 de octubre de 2012

JSTL: Ruta actual de una página

Para obtener por medio de JSTL el path actual de una página incluyendo el query string, si lo tiene:

<c:set var="url"><c:out value="${pageContext.request.scheme}://${pageContext.request.serverName}${pageContext.request.contextPath}${pageContext.request.servletPath}"/><c:if test="${not empty pageContext.request.queryString}"><c:out value="?${pageContext.request.queryString}"/></c:if>
</c:set>

lunes, 1 de octubre de 2012

Prevención de XSS (Cross-site scripting)

Dejo a disposición un pequeño ejemplo de como evitar del lado del cliente, haciendo la aclaración de que no debemos omitir la validación del lado del servidor, un ataque de seguridad usando el Cross-site scripting (XSS).

Esta vulnerabilidad se da cuando una aplicación web toma datos que el usuario suministra y los despliega en una página HTML. Si el usuario suministra datos que contiene caracteres que son interpretados como parte de un elemento HTML en lugar de texto literal, entonces un atacante puede modificar el HTML que es recibido por el navegador de la victima.

Para poder explotar esta vulnerabilidad, un usuario malicioso debe engañar a una victima para que visite un URL con la carga de XSS. Una carga de XSS puede consistir de HTML, JavaScript u otro tipo de contenido que pueda ser interpretado en el navegador. 

Este tipo de ataque apunta a usuarios de una aplicación web, en lugar de la aplicación web misma. Puede llevar al robo de credenciales de usuario e información financiera personal.

En el siguiente script recorro todos los elementos del formulario para reemplazar los paréntesis angulares ("<>") por sus códigos HTML equivalentes. Repito que este filtro debe aplicarse también del lado del servidor, pues un atacante podría crear un formulario propio sin estas validaciones y redirigir a una victima a esta versión maliciosa del formulario.

<html>
<head>
 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
 <script type="text/javascript">
 $(document).ready(function() {
   $('#formId').submit(function() {    
  var $inputs = $('#formId :input');    
  var values = {};
   $inputs.each(function() {
    var newValue=$(this).val().replace("<", "&lt;").replace(">", "&gt;");  
    $(this).val(newValue);
   });
  });
 });
 </script>
 </head>
<body>
 <form id="formId" action="sucess.html" method="get">
  user:<input type="text" name="username" />
  Password:<input type="password" name="password" />
  <input type="submit" />
 </form>
</body>
</html>