Subsecciones


Práctica: Comma Separated Values. CSV

Donde

[~/srcPLgrado/csv(master)]$ pwd -P
/Users/casiano/local/src/javascript/PLgrado/csv

Véase https://bitbucket.org/casiano/pl-grado-csv/src y https://github.com/crguezl/csv.

Introducción al formato CSV

Véase Comma Separated Values en la Wikipedia:

A comma-separated values (CSV) file stores tabular data (numbers and text) in plain-text form. A CSV file consists of any number of records, separated by line breaks of some kind; each record consists of fields, separated by a comma. All records have an identical sequence of fields.

Ejemplo de ejecución

Véase la página en http://crguezl.github.io/csv/. Pruebe a dar como entrada cualquiera de estas dos

[~/srcPLgrado/csv(gh-pages)]$ cat input.txt 
"producto",           "precio"
"camisa",             "4,3"
"libro de O\"Reilly", "7,2"

[~/srcPLgrado/csv(gh-pages)]$ cat input2.txt 
"producto",           "precio"  "fecha"
"camisa",             "4,3",    "14/01"
"libro de O\"Reilly", "7,2"     "13/02"

Pruebe también a dar alguna entrada errónea.

Figura: Ejemplo de pantalla de La aplicación para el Análisis de Datos en Formato CSV
\begin{figure}\begin{center}
\centerline{\epsfig{file=chapter2/csv.eps, width=17cm}}
\end{center}
\end{figure}

Aproximación al análisis mediante expresiones regulares de CSV

Una primera aproximación sería hacer split por las comas:
> x = '"earth",1,"moon",9.374'
'"earth",1,"moon",9.374'
> y = x.split(/,/)
[ '"earth"', '1', '"moon"', '9.374' ]

Esta solución deja las comillas dobles en los campos entrecomillados. Peor aún, los campos entrecomillados pueden contener comas, en cuyo caso la división proporcionada por split sería errónea:

> x = '"earth, mars",1,"moon, fobos",9.374'
'"earth, mars",1,"moon, fobos",9.374'
> y = x.split(/,/)
[ '"earth', ' mars"', '1', '"moon', ' fobos"', '9.374' ]

La siguiente expresión regular reconoce cadenas de comillas dobles con secuencias de escape seguidas opcionalmente de una coma:

> x = '"earth, mars",1,"moon, fobos",9.374'
'"earth, mars",1,"moon, fobos",9.374'
> r = /"((?:[^"\\]|\\.)*)"\s*,?/g
/"((?:[^"\\]|\\.)*)"\s*,?/g
> w = x.match(r)
[ '"earth, mars",', '"moon, fobos",' ]
If your regular expression uses the g flag, you can use the exec or match methods multiple times to find successive matches in the same string. When you do so, the search starts at the substring of string specified by the regular expression's lastIndex property.

Javascript sub-matches stop working when the g modifier is set:

> text = 'test test test test'
'test test test test'
> text.match(/t(e)(s)t/)
[ 'test', 'e', 's', index: 0, input: 'test test test test' ]
> text.match(/t(e)(s)t/g)
[ 'test', 'test', 'test', 'test' ]
Sin embargo el método exec de las expresiones regulares si que conserva las subexpresiones que casan con los paréntesis:
> r = /t(e)(s)t/g
/t(e)(s)t/g
> text = 'test test test test'
'test test test test'
> while (m = r.exec(text)) {
... console.log(m);
... }
[ 'test', 'e', 's', index: 0, input: 'test test test test' ]
[ 'test', 'e', 's', index: 5, input: 'test test test test' ]
[ 'test', 'e', 's', index: 10, input: 'test test test test' ]
[ 'test', 'e', 's', index: 15, input: 'test test test test' ]
undefined
Another catch to remember is that exec() doesn't return the matches in one big array: it keeps returning matches until it runs out, in which case it returns null.

Véase

Esta otra expresión regular /([^,]+),?|\s*,/ actúa de forma parecida al split. Reconoce secuencias no vacías de caracteres que no contienen comas seguidas opcionalmente de una coma o bien una sóla coma (precedida opcionalmente de blancos):

> x = '"earth, mars",1,"moon, fobos",9.374'
'"earth, mars",1,"moon, fobos",9.374'
> r = /([^,]+),?|\s*,/g
/([^,]+),?|\s*,/g
> w = x.match(r)
[ '"earth,', ' mars",', '1,', '"moon,', ' fobos",', '9.374' ]

La siguiente expresión regular es la unión de dos:

>  x = '"earth, mars",1,"moon, fobos",9.374'
'"earth, mars",1,"moon, fobos",9.374'
> r = /\s*"((?:[^"\\]|\\.)*)"\s*,?|\s*([^,]+),?|\s*,/g
/\s*"((?:[^"\\]|\\.)*)"\s*,?|\s*([^,]+),?|\s*,/g
> w = x.match(r)
[ '"earth, mars",', '1,', '"moon, fobos",', '9.374' ]
El operador | trabaja en circuito corto:
> r = /(ba?)|(b)/
/(ba?)|(b)/
> r.exec("ba")
[ 'ba', 'ba', undefined, index: 0, input: 'ba' ]
> r = /(b)|(ba?)/
/(b)|(ba?)/
> r.exec("ba")
[ 'b', 'b', undefined, index: 0, input: 'ba' ]

Si usamos exec tenemos:

> x = '"earth, mars",1,"moon, fobos",9.374'
'"earth, mars",1,"moon, fobos",9.374'
> r = /\s*"((?:[^"\\]|\\.)*)"\s*,?|\s*([^,]+),?|\s*,/g
/\s*"((?:[^"\\]|\\.)*)"\s*,?|\s*([^,]+),?|\s*,/g
> while (m = r.exec(x)) { console.log(m); }
[ '"earth, mars",', 'earth, mars', undefined, index: 0,
  input: '"earth, mars",1,"moon, fobos",9.374' ]
[ '1,', undefined, '1', index: 14,
  input: '"earth, mars",1,"moon, fobos",9.374' ]
[ '"moon, fobos",', 'moon, fobos', undefined, index: 16,
  input: '"earth, mars",1,"moon, fobos",9.374' ]
[ '9.374', undefined, '9.374', index: 30,
  input: '"earth, mars",1,"moon, fobos",9.374' ]
undefined

Es necesario tener en cuenta el caso de los campos vacíos, especialmente al principio y al final:

> w = ',"earth, mars",1,"moon,fobos",   ,9.374,'
',"earth, mars",1,"moon,fobos",   ,9.374,'
Al analizar w debería haber un campo vacío al principio, uno en medio y otro al final.

Para resolverlo hemos usado lookahead (^|,)(?=\s*(,|$)) que dice que una coma seguida de una coma o el final da lugar a un nuevo campo (y que si al comienzo de la cadena hay una coma también se produce un nuevo campo):

> r
/\s*"((?:[^"\\]|\\.)*)"\s*|\s*([^,]+)|(^|,)(?=\s*(,|$))/g
> w.match(r)
[ '', '"earth, mars"', '1', '"moon,fobos"', ',', '   ', '9.374', ',' ]

RegExp Objects

  1. RegExp Objects

    The RegExp constructor creates a regular expression object for matching text with a pattern.

    Literal and constructor notations are possible:

    /pattern/flags; 
    new RegExp(pattern [, flags]);
    

  2. RegExp.prototype.exec

    The exec() method executes a search for a match in a specified string. Returns a result array, or null.

    If you are executing a match simply to find true or false, use the RegExp.prototype.test() method or the String.prototype.search() method.

  3. String.prototype.search

    str.search(regexp)

    If successful, search returns the index of the regular expression inside the string. Otherwise, it returns -1.

    When you want to know whether a pattern is found in a string use search (similar to the regular expression test method); for more information (but slower execution) use match (similar to the regular expression exec method).

  4. String.prototype.match
  5. String.prototype.replace

    The replace() method returns a new string with some or all matches of a pattern replaced by a replacement. The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match.

    > re = /apples/gi
    /apples/gi
    > str = "Apples are round, and apples are juicy."
    'Apples are round, and apples are juicy.'
    > newstr = str.replace(re, "oranges")
    'oranges are round, and oranges are juicy.'
    

    The replacement string can be a function to be invoked to create the new substring (to put in place of the substring received from parameter #1). The arguments supplied to this function are:

    Possible name Supplied value
    match The matched substring. (Corresponds to $&.)
    p1, p2, ... The nth parenthesized submatch string, provided the first argument to replace was a RegExp object. (Corresponds to $1, $2, etc.) For example, if /(\a+)(\b+)/, was given, p1 is the match for \a+, and p2 for \b+.
    offset The offset of the matched substring within the total string being examined. (For example, if the total string was abcd, and the matched substring was bc, then this argument will be 1.)
    string The total string being examined

    [~/javascript/learning]$ pwd -P
    /Users/casiano/local/src/javascript/learning
    [~/javascript/learning]$ cat f2c.js 
    #!/usr/bin/env node
    function f2c(x)
    {
      function convert(str, p1, offset, s)
      {
        return ((p1-32) * 5/9) + "C";
      }
      var s = String(x);
      var test = /(\d+(?:\.\d*)?)F\b/g;
      return s.replace(test, convert);
    }
    
    var arg = process.argv[2] || "32F";
    console.log(f2c(arg));
    

    [~/javascript/learning]$ ./f2c.js 100F
    37.77777777777778C
    [~/javascript/learning]$ ./f2c.js 
    0C
    

index.html

Los dos hechos mas relevantes en este index.html son el uso de las librerías jquery, underscore y el uso de un elemento textarea en vez de un input para la entrada:
<html>
  <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <title>CSV Analyzer</title>
     <link href="global.css" rel="stylesheet" type="text/css">

     <script type="text/javascript" src="../../underscore/underscore.js"></script>
     <script type="text/javascript" src="../../jquery/starterkit/jquery.js"></script>
     <script type="text/javascript" src="csv.js"></script>
  </head>
  <body>
    <h1>Comma Separated Value Analyzer</h1>
    <div>
      <i>Write a CSV string. Click the table button. The program outputs a table with the specified data.</i>
    </div>
    <table>
      <tr>
        <th>CSV string:</th> <!-- autofocus attribute is HTML5 -->
        <td><textarea autofocus cols = "80" rows = "5" id="original"></textarea></td> 
      </tr>
    </table>
    <button type="button">table:</button><br>
    <span class="output" id="finaltable"></span>
  </body>
</html>

jQuery

jQuery (Descarga la librería)

jQuery is a cross-platform JavaScript library designed to simplify the client-side scripting of HTML.

How JQuery Works

~/javascript/jquery(master)]$ cat index.html 
<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Demo</title>
</head>
<body>
    <a href="http://jquery.com/">jQuery</a>
    <script src="starterkit/jquery.js"></script>
    <script>
 
    // Your code goes here.
 
    </script>
</body>
</html>

To ensure that their code runs after the browser finishes loading the document, many JavaScript programmers wrap their code in an onload function:

window.onload = function() { alert( "welcome" ); }

Unfortunately, the code doesn't run until all images are finished downloading, including banner ads. To run code as soon as the document is ready to be manipulated, jQuery has a statement known as the ready event:

$( document ).ready(function() {
    // Your code here.
});
For click and most other events, you can prevent the default behavior by calling event.preventDefault() in the event handler. If this method is called, the default action of the event will not be triggered. For example, clicked anchors will not take the browser to a new URL.

[~/javascript/jquery(master)]$ cat index2.html 
<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Demo</title>
</head>
<body>
    <a href="http://jquery.com/">jQuery</a>
    <script src="starterkit/jquery.js"></script>
    <script>
 
    $( document ).ready(function() {
        $( "a" ).click(function( event ) {
            alert( "The link will no longer take you to jquery.com" );
            event.preventDefault();
        });
    });
 
    </script>
</body>
</html>
Borrowing from CSS 1–3, and then adding its own, jQuery offers a powerful set of tools for matching a set of elements in a document.

See jQuery Category: Selectors.

Another common task is adding or removing a class. jQuery also provides some handy effects.

[~/javascript/jquery(master)]$ cat index3.html 
<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <style>
        a.test { font-weight: bold; }
    </style>
    <title>Demo</title>
</head>
<body>
    <a href="http://jquery.com/">jQuery</a>
    <script src="starterkit/jquery.js"></script>
    <script>
 
    $( document ).ready(function() {
        $( "a" ).click(function( event ) {
            $( "a" ).addClass( "test" );
            alert( "The link will no longer take you to jquery.com" );
            event.preventDefault();
            $( "a" ).removeClass( "test" );
            $( this ).hide( "slow" );
            $( this ).show( "slow" );
        });
    });
 
    </script>
</body>
</html>

Ejemplo usando Ajax con jQuery y Express.js

Código del server:

[~/javascript/jquery/how-jquery-works-tutorial(getallparams)]$ cat app.js
var express = require('express');
var app = express();
var path = require('path');

app.use(express.static('public'));

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.get('/', function (req, res) {
  res.render('index', { title: 'Express' });
})

app.get('/chuchu', function (req, res) {
  var isAjaxRequest = req.xhr;
  console.log(isAjaxRequest);
  if (isAjaxRequest) {
    console.log(req.query);
    res.send('{"answer": "Server responds: hello world!"}')
  }
  else {
    res.send('not an ajax request');
  }
});

var server = app.listen(3000, function () {

  var host = server.address().address
  var port = server.address().port

  console.log('Example app listening at http://%s:%s', host, port)

});

To use callbacks, it is important to know how to pass them into their parent function.

En el directorio views hemos puesto el template:

[~/javascript/jquery/how-jquery-works-tutorial(getallparams)]$ cat views/index.ejs 
<!doctype html>
<html>
  <head>
    <title><%- title %></title>
  </head>
  <body>
    <h1><%- title  %></h1>
    <ul>
      <li><a href="http://jquery.com/" id="jq">jQuery</a>
      <li><a href="/chuchu">Visit chuchu!</a>
    </ul>
    <div class="result"></div>
    <script src="https://code.jquery.com/jquery-2.1.3.js"></script>
    <script>
      $( document ).ready(function() {
          $( "#jq" ).click(function( event ) {
              event.preventDefault();
              $.get( "/chuchu", {nombres: ["juan", "pedro"]}, function( data ) {
                $( ".result" ).html( data["answer"]);
                console.log(data);
              }, 'json');
          });
      });
    </script>
  </body>
</html>

Estas son las dependencias:

[~/javascript/jquery/how-jquery-works-tutorial(getallparams)]$ cat package.json 
{
  "name": "ajaxjquery",
  "version": "0.0.0",
  "description": "",
  "main": "hello.js",
  "dependencies": {
    "express": "*",
    "ejs": "*",
    "gulp-shell": "*",
    "body-parser": "~1.12.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "node hello.js"
  },
  "author": "",
  "license": "ISC"
}
Además hemos instalado a nivel global gulp y node-supervisor.

Podemos arrancar el servidor usando este gulpfile:

[~/javascript/jquery/how-jquery-works-tutorial(getallparams)]$ cat gulpfile.js 
var gulp    = require('gulp');
var shell = require('gulp-shell');

gulp.task('default', ['server']);

// npm install supervisor -g
gulp.task('server', function () {
  return gulp.src('').pipe(shell([ 'node-supervisor app.js' ]));
});

gulp.task('open', function() {
  return gulp.src('').
           pipe(shell("open https://github.com/crguezl/how-jquery-works-tutorial/tree/getallparams"));
});

Ejemplo de como Desplegar una Aplicación Express sobre Node.JS en Heroku

Véase:

Ajax, jQuery y Sinatra

JavaScript enables you to freely pass functions around to be executed at a later time. A callback is a function that is passed as an argument to another function and is usually executed after its parent function has completed.

Callbacks are special because they wait to execute until their parent finishes or some event occurs.

Meanwhile, the browser can be executing other functions or doing all sorts of other work.

[~/javascript/jquery(master)]$ cat app.rb
require 'sinatra'

set :public_folder, File.dirname(__FILE__) + '/starterkit'

get '/' do
  erb :index
end

get '/chuchu' do
  if request.xhr?
    "hello world!"
  else 
    erb :tutu
  end
end

__END__

@@layout
  <!DOCTYPE html>
  <html>
    <head>
        <meta charset="utf-8" />
        <title>Demo</title>
    </head>
    <body>
        <a href="http://jquery.com/">jQuery</a>
        <div class="result"></div>
        <script src="jquery.js"></script>
        <%= yield %>
    </body>
  </html>

@@index
  <script>
  $( document ).ready(function() {
      $( "a" ).click(function( event ) {
          event.preventDefault();
          $.get( "/chuchu", function( data ) {
            $( ".result" ).html( data );
            alert( "Load was performed." );
          });
      });
  });
  </script>

@@tutu
  <h1>Not an Ajax Request!</h1>

To use callbacks, it is important to know how to pass them into their parent function.

Executing callbacks with arguments can be tricky.

This code example will not work:

$.get( "myhtmlpage.html", myCallBack( param1, param2 ) );
The reason this fails is that the code executes

myCallBack( param1, param2)

immediately and then passes myCallBack()'s return value as the second parameter to $.get().

We actually want to pass the function myCallBack, not myCallBack( param1, param2 )'s return value (which might or might not be a function).

So, how to pass in myCallBack() and include arguments?

To defer executing myCallBack() with its parameters, you can use an anonymous function as a wrapper.

[~/javascript/jquery(master)]$ cat app2.rb
require 'sinatra'

set :public_folder, File.dirname(__FILE__) + '/starterkit'

get '/' do
  erb :index
end

get '/chuchu' do
  if request.xhr? # is an ajax request
    "hello world!"
  else 
    erb :tutu
  end
end

__END__

@@layout
  <!DOCTYPE html>
  <html>
    <head>
        <meta charset="utf-8" />
        <title>Demo</title>
    </head>
    <body>
        <a href="http://jquery.com/">jQuery</a>
        <div class="result"></div>
        <script src="jquery.js"></script>
        <%= yield %>
    </body>
  </html>

@@tutu
  <h1>Not an Ajax Request!</h1>

@@index
  <script>
    var param = "chuchu param";
    var handler = function( data, textStatus, jqXHR, param ) {
      $( ".result" ).html( data );
      alert( "Load was performed.\n"+
             "$data = "+data+
             "\ntextStatus = "+textStatus+
             "\njqXHR = "+JSON.stringify(jqXHR)+
             "\nparam = "+param );
    };
    $( document ).ready(function() {
        $( "a" ).click(function( event ) {
            event.preventDefault();
            $.get( "/chuchu", function(data, textStatus, jqXHR ) {
              handler( data, textStatus, jqXHR, param);
            });
        });
    });
  </script>
El ejemplo en app2.rb puede verse desplegado en Heroku: http://jquery-tutorial.herokuapp.com/

JSON.stringify()

The JSON.stringify() method converts a value to JSON , optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified.

JSON.stringify(value[, replacer [, space]])

See another example of use in http://jsfiddle.net/casiano/j7tsF/. To learn to use JSFiddle wath the YouTube video How to use JSFiddle by Jason Diamond

Underscore

Underscore: is a utility-belt library for JavaScript that provides a lot of the functional programming support that you would expect in Ruby.

Templates en Underscore

Content delivery network or content distribution network (CDN)

Una CDN que provee underscore esta en http://cdnjs.com/:

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

A content delivery network or content distribution network (CDN) is a large distributed system of servers deployed in multiple data centers across the Internet. The goal of a CDN is to serve content to end-users with high availability and high performance. CDNs serve a large fraction of the Internet content today, including

Google provee también un servicio CDN para los desarrolladores en https://developers.google.com/speed/libraries/devguide.

textarea, autofocus y button

  1. textarea:

    The <textarea> tag defines a multi-line text input control.

    A text area can hold an unlimited number of characters, and the text renders in a fixed-width font (usually Courier).

    The size of a text area can be specified by the cols and rows attributes, or through CSS' height and width properties.

    cols and rows consider the font size. height and width aren't.

  2. autofocus.

    The autofocus attribute is a boolean attribute.

    When present, it specifies that the text area should automatically get focus when the page loads.

    Véase también [1]

  3. button:

    The <button> tag defines a clickable button.

    Inside a <button> element you can put content, like text or images.

Local Storage (HTML5 Web Storage)

Web storage and DOM storage (document object model) are web application software methods and protocols used for storing data in a web browser.

Véase:

While Chrome does not provide a UI for clearing localStorage, there is an API that will either clear a specific key or the entire localStorage object on a website.

//Clears the value of MyKey
window.localStorage.clear("MyKey");

//Clears all the local storage data
window.localStorage.clear();
Once done, localStorage will be cleared. Note that this affects all web pages on a single domain, so if you clear localStorage for jsfiddle.net/index.html (assuming that's the page you're on), then it clears it for all other pages on that site.

global.css

html *
{
   font-size: large; 
   /* The !important ensures that nothing can override what you've set in this style (unless it is also important). */
   font-family: Arial;
}

h1            { text-align: center; font-size: x-large; }
th, td        { vertical-align: top; text-align: right; }   
/* #finaltable  * { color: white; background-color: black; }   */

/* #finaltable table { border-collapse:collapse; } */
/* #finaltable table, td { border:1px solid white; } */
#finaltable:hover td { background-color: blue; } 
tr:nth-child(odd)    { background-color:#eee; }
tr:nth-child(even)    { background-color:#00FF66; }
input        { text-align: right;  border: none;       }     /* Align input to the right  */
textarea     { border: outset; border-color: white;       }                        
table        { border: inset; border-color: white; }
table.center { margin-left:auto; margin-right:auto; }
#result      { border-color: red; }
tr.error       { background-color: red; }
body
{
 background-color:#b0c4de;  /* blue */
}

  1. Introducción a las pseudo clases de CSS3

    Una pseudo clase es un estado o uso predefinido de un elemento al que se le puede aplicar un estilo independientemente de su estado por defecto. Existen cuatro tipos diferentes de pseudo clases:

  2. CSS pattern matching

    In CSS, pattern matching rules determine which style rules apply to elements in the document tree. These patterns, called selectors, may range from simple element names to rich contextual patterns. If all conditions in the pattern are true for a certain element, the selector matches the element.

    The universal selector, written *, matches the name of any element type. It matches any single element in the document tree.

    For example, this rule set will be applied to every element in a document:

    * {
      margin: 0;
      padding: 0;
    }
    
  3. CSS class selectors

    Working with HTML, authors may use the period (.) notation as an alternative to the ~= notation when representing the class attribute. Thus, for HTML, div.value and div[class~=value] have the same meaning. The attribute value must immediately follow the period (.).

  4. CSS3: nth-child() selector The :nth-child(n) selector matches every element that is the nth child, regardless of type, of its parent.

    n can be a number, a keyword, or a formula.

  5. The CSS border properties allow you to specify the style and color of an element's border. The border-style property specifies what kind of border to display. For example, inset: Defines a 3D inset border while :outset defines a 3D outset border. The effect depends on the border-color value

    See CSS: border

csv.js

// See http://en.wikipedia.org/wiki/Comma-separated_values
"use strict"; // Use ECMAScript 5 strict mode in browsers that support it

$(document).ready(function() {
   $("button").click(function() {
     calculate();
   });
 });

function calculate() {
  var result;
  var original       = document.getElementById("original");
  var temp = original.value;
  var regexp = /_____________________________________________/g;
  var lines = temp.split(/\n+\s*/);
  var commonLength = NaN;
  var r = [];
  // Template using underscore
  var row = "<%% _.each(items, function(name) { %>"     +
            "                    <td><%%= name %></td>" +
            "              <%% }); %>";

  if (window.localStorage) localStorage.original  = temp;
  
  for(var t in lines) {
    var temp = lines[t];
    var m = temp.match(regexp);
    var result = [];
    var error = false;
    
    if (m) {
      if (commonLength && (commonLength != m.length)) {
        //alert('ERROR! row <'+temp+'> has '+m.length+' items!');
        error = true;
      }
      else {
        commonLength = m.length;
        error = false;
      }
      for(var i in m) {
        var removecomma = m[i].replace(/,\s*$/,'');
        var remove1stquote = removecomma.replace(/^\s*"/,'');
        var removelastquote = remove1stquote.replace(/"\s*$/,'');
        var removeescapedquotes = removelastquote.replace(/\"/,'"');
        result.push(removeescapedquotes);
      }
      var tr = error? '<tr class="error">' : '<tr>';
      r.push(tr+_.template(row, {items : result})+"</tr>");
    }
    else {
      alert('ERROR! row '+temp+' does not look as legal CSV');
      error = true;
    }
  }
  r.unshift('<p>\n<table class="center" id="result">');
  r.push('</table>');
  //alert(r.join('\n')); // debug
  finaltable.innerHTML = r.join('\n');
}

window.onload = function() {
  // If the browser supports localStorage and we have some stored data
  if (window.localStorage && localStorage.original) {
    document.getElementById("original").value = localStorage.original;
  }
};
    

  1. Tutorials:Getting Started with jQuery

Resumen de Requisitos/Tareas

Además de todo lo descrito en los apartados anteriores, la práctica debe cumplir con los siguientes requisitos:

Casiano Rodríguez León
2016-03-27