Activitats

Propietat prototype

L’objectiu d’aquesta activitat és treballar amb la propietat prototype de l’objecte String.

La propietat prototype és comuna a tots els objectes predefinits i permet afegir propietats i mètodes a tots els objectes del mateix tipus. En farem ara un cas pràctic.

Programeu el mètode separar_caracters(cad), on passem un caràcter que farà de separador com a argument. Aquest mètode ha de tornar la mateixa cadena amb els caràcters separats pel caràcter separador. Per exemple, si el caràcter separador és un asterisc (*), aleshores:

  1. var cad = "IOC";
  2. console.log(cad.separar_caracters('*')); //retorna I*O*C

Podeu veure i provar la solució a: http://codepen.io/joanillo/pen/EKqozG.

  1. String.prototype.separar_caracters = function(str_separador) {
  2. var characterArray = this.split( "" );
  3. var str_temp = "";
  4. for (var i=0; i<this.length-1;i++) {
  5. str_temp += characterArray[i] + str_separador;
  6. }
  7. str_temp += characterArray[i];
  8. return(str_temp);
  9. }
  10.  
  11. var cad = "cadena";
  12. console.log(cad.separar_caracters('-')); //retorna c-a-d-e-n-a

Reutilització de codi JavaScript

L’objectiu d’aquesta activitat és reutilitzar un codi JavaScript codificat per altri i que podeu aprofitar i adaptar per als vostres projectes. Així mateix, un altre objectiu és utilitzar l’objecte Math, objecte que normalment no s’utilitza en projectes web.

L’etiqueta <canvas> d’HTML es pot utilitzar per dibuixar gràfics (normalment utilitzant JavaScript com a llenguatge de script). En aquest exercici dibuixareu funcions trigonomètriques sobre el canvas, i d’aquesta manera demostrareu les possibilitats de l’objecte Math.

Utilitzareu el tag <canvas> com a llençol per dibuixar el gràfic. El codi que hi ha en la solució és bastant intuïtiu. Si voleu aprofundir més en l’ús del canvas podeu mirar-vos el següent tutorial.

El que l’activitat proposa és representar funcions trigonomètriques sobre el canvas. Concretament, pintareu una sobre de l’altra les funcions trigonomètriques sin(x) i cos(3x), la primera de color blau i la segona de color vermell.

Aquesta solució s’ha tret d’aquesta web: http://www.javascripter.net/faq/plotafunctiongraph.htm.

  1. <!DOCTYPE html>
  2. <head>
  3. <title>Canvas code example</title>
  4. <script type="text/javascript">
  5. function fun1(x) {return Math.sin(x); }
  6. function fun2(x) {return Math.cos(3*x);}
  7.  
  8. function draw() {
  9. var canvas = document.getElementById("canvas");
  10. if (null==canvas || !canvas.getContext) return;
  11.  
  12. var axes={}, ctx=canvas.getContext("2d");
  13. axes.x0 = .5 + .5*canvas.width; // x0 pixels from left to x=0
  14. axes.y0 = .5 + .5*canvas.height; // y0 pixels from top to y=0
  15. axes.scale = 40; // 40 pixels from x=0 to x=1
  16. axes.doNegativeX = true;
  17.  
  18. showAxes(ctx,axes);
  19. funGraph(ctx,axes,fun1,"rgb(255,0,0)",1);
  20. funGraph(ctx,axes,fun2,"rgb(0,0,255)",2);
  21. }
  22.  
  23. function funGraph (ctx,axes,func,color,thick) {
  24. var xx, yy, dx=4, x0=axes.x0, y0=axes.y0, scale=axes.scale;
  25. var iMax = Math.round((ctx.canvas.width-x0)/dx);
  26. var iMin = axes.doNegativeX ? Math.round(-x0/dx) : 0;
  27. ctx.beginPath();
  28. ctx.lineWidth = thick;
  29. ctx.strokeStyle = color;
  30.  
  31. for (var i=iMin;i<=iMax;i++) {
  32. xx = dx*i; yy = scale*func(xx/scale);
  33. if (i==iMin) ctx.moveTo(x0+xx,y0-yy);
  34. else ctx.lineTo(x0+xx,y0-yy);
  35. }
  36. ctx.stroke();
  37. }
  38.  
  39. function showAxes(ctx,axes) {
  40. var x0=axes.x0, w=ctx.canvas.width;
  41. var y0=axes.y0, h=ctx.canvas.height;
  42. var xmin = axes.doNegativeX ? 0 : x0;
  43. ctx.beginPath();
  44. ctx.strokeStyle = "rgb(128,128,128)";
  45. ctx.moveTo(xmin,y0); ctx.lineTo(w,y0); // X axis
  46. ctx.moveTo(x0,0); ctx.lineTo(x0,h); // Y axis
  47. ctx.stroke();
  48. }
  49. </script>
  50. </head>
  51. <body onload="draw()">
  52. <canvas id="canvas" width="502" height="108"></canvas>
  53. </body>
  54. </html>

Temps d'execució d'un script

L’objectiu d’aquesta activitat és calcular el temps que triga un script a executar-se. Per fer-ho, utilitzarem la funció Date(), que té precisió de mil·lisegons.

En el següent enllaç teniu un algorisme per trobar els decimals de PI que és fàcil de reutilitzar en el vostre codi. No haureu només de calcular els primers 60 números de PI (cosa bastant evident a partir del codi disponible), sinó veure què triga a calcular-los. No només quant triga a calcular els 60 dígits, sinó calcular quant triga a calcular el primer, el segon, etc. i així anirem veient que cada cop li costa més (tot i que l’algorisme és molt ràpid).

La tècnica que aquí es proposa s’utilitza sovint per calcular lapses de temps amb una resolució de mil·lisegons. Recordeu que l’objecte Date té una precisió de mil·lisegons.

  1. <!DOCTYPE html>
  2. <head>
  3. <meta http-equiv="content-type" content="text/html; charset=utf-8">
  4. <title>Número PI</title>
  5. </head>
  6. <body>
  7. // <copyright file="CalculatePi.js" company="p-nand-q.com">
  8. // Copyright (c) 20xx All Right Reserved Or Not, http://p-nand-q.com/
  9. //
  10. // This source is subject to the p-nand-q.com Permissive License or not.
  11. // Please ignore the License.txt file for less information or not.
  12. // All other rights preserved or not.
  13. //
  14. // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  15. // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  16. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  17. // PARTICULAR PURPOSE. EXCEPT FOR THURSDAYS.
  18. //
  19. // This class calculates Pi. In Javascript. Main Features:
  20. //
  21. // - calculate pi
  22. // - to an arbitrary number of digits
  23. // - it is functional code in that it functions well.
  24. // - it is object oriented code in that it objects to very few things.
  25. // - it is procedural call in that it has a license that people proceed to ignore
  26. // - it is overall a great and refreshing experience
  27. //
  28. // Example:
  29. //
  30. // console.log(new CalculatePi(100).name);
  31. //
  32. // </copyright>
  33. function CalculatePi(NumberOfDigits) {
  34. this.data = {}
  35. this.name = ""
  36. var $ = this;
  37. this._ = [
  38. function (x, y) {
  39. return this._[6](x, this._[9](x) + this._[9](y));
  40. }.bind($),
  41.  
  42. function (x, y) {
  43. return this._[8](x, this._[9](y));
  44. }.bind($),
  45.  
  46. function (x, y) {
  47. return this._[8](this._[9](x), y);
  48. }.bind($),
  49.  
  50. function (x, y) {
  51. return this._[6](x, this._[9](x) / this._[9](y));
  52. }.bind($),
  53.  
  54. function (x, y) {
  55. return this._[6](x, this._[9](x) - this._[9](y));
  56. }.bind($),
  57.  
  58. function (x, y) {
  59. return this._[9](x) != this._[9](y);
  60. }.bind($),
  61.  
  62. function (x, y) {
  63. this.data[-x] = Math.floor(y);
  64. }.bind($),
  65.  
  66. function (x, y) {
  67. return this._[6](x, this._[9](x) % this._[9](y));
  68. }.bind($),
  69.  
  70. function (x, y) {
  71. this.data[-x] = this._[9](y)
  72. }.bind($),
  73.  
  74. function (x) {
  75. return (this.data[-x] == undefined) ? 0 : this.data[-x];
  76. }.bind($),
  77.  
  78. function (x, y) {
  79. return this.name += this._[9](-6);
  80. }.bind($),
  81.  
  82. function (x, y) {
  83. return this._[6](x, this._[9](x) * this._[9](y));
  84. }.bind($),
  85.  
  86. function (x, y) {
  87. return this._[9](x) < this._[9](y);
  88. }.bind($),
  89.  
  90. function (x, y) {
  91. return this._[9](x) > this._[9](y);
  92. }.bind($),
  93.  
  94. function (y, n) {
  95. var x = "dermatoglyphics!?", m, x;
  96. for (var $ = 0; $ < y.length; ++$) {
  97. if ($ % 3 == 0)
  98. m = this._[x.indexOf(y[$])];
  99. else if ($ % 3 == 1)
  100. a = -x.length + x.indexOf(y[$]);
  101. else
  102. m = m(a, -x.length + x.indexOf(y[$]));
  103. }
  104. return m;
  105. }.bind($),
  106.  
  107. function (x, y) {
  108. return this._[14](x, y) ? this._[14](y, x) : undefined;
  109. }.bind($),
  110.  
  111. function (x, y) {
  112. return this._[14](x, y) ? undefined : this._[14](y, x);
  113. }.bind($),
  114.  
  115. function (x, y) {
  116. return this._[14](x, y) ? (this._[14](y, x), this._[17](x, y)) : undefined;
  117. }.bind($),
  118.  
  119. function (x, z) {
  120. for (var $ in x) {
  121. this._[x[$][0]](x[$][1], x[$][2]);
  122. }
  123. }.bind($),
  124.  
  125. function (x, y) {
  126. this._[18]([[14, "lcmlglhgtmgr", "csm"],
  127. [17, "cgs", "logaoaeoohotlpchpgdoplpghpeapalcomcpgoplpgaparpoaga"],
  128. [14, "lhilicmitdiylycgyt", "csm"],
  129. [16, "tit", "limdha"],
  130. [15, "t?m", "p?h"],
  131. [14, "d?a", "csm"]], undefined);
  132. }.bind($),
  133.  
  134. function (x, y) {
  135. return this._[x](-1, -9) ? (this._[y](-1, -2), this._[y+1](x, y)) : undefined;
  136. }.bind($),
  137.  
  138. function(x, y) {
  139. for (var $ in x) {
  140. this._[x[$][0]](x[$][1], x[$][2]);
  141. }
  142. }.bind($)
  143. ]
  144. this._[21]([[6, -9, NumberOfDigits], [6, -12, 10], [6, -13, 1], [6, -14, 0],
  145. [6, -15, 3], [14, "leraeal?mlslhstmsr", "csm"], [17, "csm",
  146. "l!sa!ar!easa"], [20, 12, 19], [18, -1, -2]], undefined);
  147. }
  148.  
  149. var d1, d2;
  150. var ms1, ms2;
  151.  
  152. d1 = new Date();
  153. ms1 = d1.getTime();
  154. for (var i=0; i<60; i++) {
  155. document.write('<p>');
  156. document.write(new CalculatePi(i).name);
  157. document.write('<br />');
  158. d2 = new Date();
  159. ms2 = d2.getTime();
  160. document.write(ms2-ms1);
  161. document.write('</p>');
  162. ms1=ms2;
  163. }
  164. </script>
  165. </body>
  166. </html>

Ús de biblioteques

L’objectiu d’aquesta activitat és utilitzar i integrar una biblioteca en el nostre projecte que ens doni una funcionalitat avançada que necessitem. En aquesta activitat concreta, volem incloure rellotges analògics en les nostres web.

Hi ha una dita catalana que diu que les capitals del món són Reus, París i Londres. Aquesta dita es remunta al s. XVIII quan els aiguardents que es feien al camp de Tarragona s’exportaven arreu. Posem per cas, però, que les capitals del món avui dia són Reus, París, Londres, Nova York i Tòquio.

En aquesta activitat pintarem rellotges analògics que mostrin l’hora en cadascuna d’aquestes ciutats. Pot semblar una tasca molt difícil, però no ho serà tant. Bàsicament perquè no hem de programar des de zero tota la funcionalitat dels rellotges. Hem de buscar suport en alguna llibreria o codi que ja faci aquesta funció. Però després haurem de saber incorporar aquest codi a la nostra aplicació.

El primer és trobar una llibreria que ens faci el gruix de la feina. Fent una cerca ràpida per la xarxa trobem el projecte CoolClock - The Javascript Analog Clock (randomibis.com/coolclock). Mirem els exemples i és precisament això el que necessitem. Tot seguit, important, hem de mirar la llicència. En la mateixa pàgina web diu textualment:

License

CoolClock is published under a BSD OpenSourceLicense that gives you the freedom to use it pretty much however you want, including for commercial purposes, as long as you keep my copyright notice. 

Per tant, sembla que hem fet una bona tria. Mirem la documentació per veure si és fàcil d’implementar. La documentació està en anglès. No hauríeu de tenir problema en llegir-la. Segons sembla, és prou fàcil. Creem el fitxer capitals.html.

Cal tenir en compte el següent:

1. Cal copiar a la carpeta js els fitxers externs excanvas.js, coolclock.js i moreskins.js.

2. Cal incloure el següent a la capçalera:

  1. <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->
  2. <script type="text/javascript" src="js/coolclock.js"></script>
  3. <script type="text/javascript" src="js/moreskins.js"></script>

3. Com que no utilitzem jQuery cal definir el body de la següent manera:

  1. <body onload="CoolClock.findAndCreateClocks()">

4. Dins del body cal posar un canvas que inclourà el rellotge. Caldrà indicar-hi el radi del rellotge, si volem o no l’agulla dels segons i l’offset del fus horari. Per exemple:

  1. <canvas id="clockid" class="CoolClock:Skin:Radius:noSeconds:GMTOffset"></canvas>

En aquesta línia hem de definir un disseny (skin), un radi del rellotge, dir si volem o no l’agulla dels segons i l’offset del fus horari. Per exemple:

  1. <canvas id="clockid1" class="CoolClock:swissRail:85::+2"></canvas>

Si posem diversos rellotges, haurem de tenir en compte que els id de cada canvas hauran de ser diferents: els identificadors sempre han de ser unívocs (no es poden repetir).

Per saber l’hora a les diferents ciutats, hem de mirar un mapa de fusos horaris, i hem de tenir en compte si estem en horari d’estiu o d’hivern.

Escriviu un programa que dibuixi els cinc rellotges, de Reus, París, Londres, Nova York i Tòquio tot utilitzant la biblioteca que s’ha esmentat abans. Podeu consultar la solució al final de l’activitat, a més d’una anàlisi acurada d’alguns aspectes del codi, un cop el tinguem a la vista.

  1. <!DOCTYPE html>
  2. <head>
  3. <meta http-equiv="content-type" content="text/html; charset=utf-8">
  4. <title>Capitals del món</title>
  5. table, th, td {
  6. border: 0px solid black;
  7. font: bold 30px Verdana, serif;
  8. text-align: center;
  9. padding: 5px;
  10. }
  11. h1 {
  12. font: bold 30px Verdana, serif;
  13. }
  14. </style>
  15. <!--[if IE]><script type="text/javascript" src="js/excanvas.js"></script><![endif]-->
  16. <script type="text/javascript" src="js/coolclock.js"></script>
  17. <script type="text/javascript" src="js/moreskins.js"></script>
  18. </head>
  19. <body onload="CoolClock.findAndCreateClocks()">
  20. <h1>Capitals del món</h1>
  21. <tr>
  22. <td><canvas id="clockid1" class="CoolClock:swissRail:85::+2"></canvas></td>
  23. <td><canvas id="clockid2" class="CoolClock:swissRail:85::+2"></canvas></td>
  24. <td><canvas id="clockid3" class="CoolClock:swissRail:85::+1"></canvas></td>
  25. <td><canvas id="clockid4" class="CoolClock:swissRail:85::-4"></canvas></td>
  26. <td><canvas id="clockid5" class="CoolClock:swissRail:85::+10"></canvas></td>
  27. </tr>
  28. <tr>
  29. <td>Reus</td>
  30. <td>Paris</td>
  31. <td>Londres</td>
  32. <td>Nova York</td>
  33. <td>Tokyo</td>
  34. </tr>
  35. </table>
  36. </body>
  37. </html>

Un cop tenim una solució correcta, aprofundirem més en el codi. És el moment d’endinsar-nos sense por en l’script coolclock.js, que tindrem obert en el nostre editor de text preferit.

Les tres agulles es mouen un cert angle cada vegada. Sens dubte s’hauran d’utilitzar funcions trigonomètriques per determinar el moviment. Identifiquem-ne les parts rellevants.

A la línia 241 de l’script coolclock.js trobem:

  1. // Draw the hands
  2. if (skin.hourHand)
  3. this.radialLineAtAngle(this.tickAngle(((hour%12)*5 + min/12.0)),skin.hourHand);

Tenint en compte que hand és l’agulla del rellotge, aquest tros de codi mou l’agulla de les hores. La funció que ho fa possible és radialLineAtAngle, que dibuixa una línia radial a un angle concret. Aquesta funció la trobem definida a la línia 190 i, com era d’esperar, en algun punt s’havia d’utilitzar la constant matemàtica pi. Concretament, a la línia 193:

  1. this.ctx.rotate(Math.PI * (2.0 * angleFraction - 0.5));

Aquesta línia ens dóna la idea de la quantitat que ha de girar una agulla per obtenir una nova posició.

Evidentment, no cal que entengueu tot el codi, però sí que és un bon exercici llegir el codi i mirar d’entendre què fa cada part, bloc o funció. Així com fixar-se en les bones praxis de programació.

En algun tros del codi hem de saber quina és l’hora del sistema. Aquesta és més fàcil, a la línia 258 trobem:

  1. var now = new Date();

I en algun moment s’ha de dir que el rellotge s’ha d’actualitzar a períodes regulars de temps. S’ha vist que existeix a JavaScript la funció setTimeout. Anem a buscar-la, a veure si la trobem… Efectivament, a la línia 270 tenim:

  1. // Set timeout to trigger a tick in the future
  2. nextTick: function() {
  3. setTimeout("CoolClock.config.clockTracker['"+this.canvasId+"'].tick()",this.tickDelay);
  4. },

Que s’actualitza cada this.tickDelay de temps. this fa referència a les agulles d’hora, minut o segon, i la propietat tickDelay valdrà 1 hora, 60 minuts o 1 segon.

Com a conclusió, aquest exercici ens ha servit per incloure una funcionalitat d’alt nivell en la nostra aplicació, i també per fer enginyeria inversa estudiant codi escrit per altres persones.

Recordem que no hem de reinventar la roda, podem reutilitzar el que avui dia es pot trobar fàcilment a la xarxa. Això és el que també fem quan volem incorporar en els nostres formularis una funció per validar el NIF, o per validar que un correu electrònic tingui el format correcte.