Terminal.js, jugando con javascript

palabras / Facebook / Twitter / Enlace / Comentar

Fork me on GitHub

Hace no mucho tiempo se me ocurrió renovar mi portafolio con un estilo más ad hoc con mi perfil de programador. Mi idea, que ya no está vigente, fue la creación de un sistema operativo completo en javascript - jQuery y HTML y comencé haciendo la terminal.

Curioso que la terminal quedó bastante simple y usable. Nació Terminal.js, el cual está hosteado en mi Github.

Desarrollando una terminal

Esta vez no quise tomar como referencia ningún plugin ya existente para hacer este efecto, así que en el desarrollo de la primera versión, me surgieron varios retos.

Markup

Suena fácil, pero llegar a un markup y un código que cumpla con la función básica de escribir al estilo terminal, no fue sencillo; la terminal tenía que escribirse de manera natural, junto con el efecto del cursor del texto haciendo blink.

El markup lo hice de la siguiente manera:

<div id="terminal">
  <div id="handle"><p>Kinduff's Terminal</p></div>
  <div class="terminal">
    <div id="respuesta"></div>
    <span>$ </span><span class="clone"></span><span class="blink"></span>
    <input id="user" type="text" size="2" value="" />
  </div>
</div>

Donde tenemos un contenedor principal llamado #terminal, un #handle que funciona como barra de título. Un div #respuesta para imprimir las respuestas de la terminal, el primer span con el símbolo de usuario de UNIX, el segundo como contenedor de clonación y el tercero como el cursor que hace blink.

El tercer elemento es un input #user, el cual con CSS no encargaremos que no se muestre.

Básicamente la lógica que sigue es que el usuario tiene un focus hacia el input #user, al escribir, el valor del input es clonado con cada tecleo en .clone, entre el símbolo de dolar y el cursor.

Javascript

La lógica detrás de cómo hacer el código escalable y fácil de mantener fue algo complicado, pero al final me basé en dos archivos: "Main" para funciones generales y "Views" para las preguntas y respuestas de la terminal.

Main.js

Cubre funciones como el focus, el escrito y clonación de texto en contenedores, el "submit" a la terminal para recibir una respuesta, el efecto drag&drop y el efecto blink del cursor. Puedes revisar el código en el repositorio.

$(document).ready(function() {
  function focus() { $('#user').focus(); }
  $('.terminal').show(); focus();
  // Focus, focus everywhere.
  $(window).focus(function() {
    focus();
    $('#user').val($('#user').val());
  });
  $(document).click(function(e) {
    focus();
    $('#user').val($('#user').val());
  });
  $('#user').on('input',function(e){
    $('.clone').text($(this).val());
  });
  // Si es usuario presiona alguna tecla
  $('#user').keypress(function(e) {
    // Si es usuario presiona enter
    if(e.keyCode===13){
      e.preventDefault();
      var valor = $(this).val(); // Rescatamos valor
      $(this).val(''); // Hacemos clear al input
      $('.clone').text(''); // Hacemos clear al clon
      $('#respuesta').append('<p class="comando">'+valor+'</p>'); // Simulamos metida del comando en contenedor
      caso = cases.indexOf(valor); // views.js -> tomamos valor del array
      if (caso === -1) { // Si no existe el comando regresa -1
        $('#respuesta').append('<p class="respuesta">Comando "'+valor+'" no identificado.<br />Para ver lista de comandos, escribe: help</p>');
      } else { 
        terminal(caso); // Si existe regresa 1..oo, ejecutamos función terminal
      }
    }
  });
  function terminal(caso) {
    // Comando es un objeto, accedemos al caso
    if (comando[caso]) { // Si el caso tiene valor
      var x = comando[caso]; // asignamos a variable
      $('#respuesta').append('<p class="respuesta">'+x+'</p>'); // Simulamos respuesta
    } else { // Si no, es el primer comando, es decir, comando clear (limpia pantalla)
      $('#respuesta, .info').empty(); // Pasamos un trapito
    }
  }
  // Función para hacer blink al cursor
  // http://stackoverflow.com/questions/12903594/set-css-with-jquery-after-ajax
  $('.blink').each(function() {
    var elem = $(this);
    setInterval(function() {
      if (elem.css('visibility') == 'hidden') {
        elem.css('visibility', 'visible');
      } else {
        elem.css('visibility', 'hidden');
      }
    }, 500);
  });
  // Para tomar la barra superior, simulamos drag en mouse
  $("#handle").live("mousedown", function () { // Click!
    $(this).css('cursor','move'); // Drag
  }).live("mouseup", function () { // Click-off...
    $(this).css('cursor','default'); // Default
  });
  // Handler con dom-drag.js
  // DYNAMIC DRIVE FUCK YEAH!
  // http://www.dynamicdrive.com/dynamicindex11/domdrag/
  var theHandle = document.getElementById("handle");
  var theRoot = document.getElementById("terminal");
  Drag.init(theHandle, theRoot);
});

Views.js

Únicamente cubre los casos específicos que se darán en la terminal y las respuestas que deberá de entregar, imprimiendo HTML directamente. Puedes revisar el código en el repositorio.

var cases = ['clear', 'help', 'resumen', 'cv', 'portafolio', 'contacto']; // Casos
var comando = {
  1: 'Comandos disponibles:<br />clear<br />help<br />resumen<br />cv<br />portafolio<br />contacto',
  2: 'Muestra Resumen.',
  3: 'Muestra CV.',
  4: 'Muestra Portafolio.',
  5: 'Muestra Contacto.'
} // Respuesta a comandos

Demo

Terminal.js

El demo lo puedes ver siguiendo este enlace.


Comentarios

Copyright © 2012-2013 AbarcaRodriguez.com