sebastien-dupire.infohttps://sebastien-dupire.info/2024-01-28T00:00:00+01:00Dev, gribouillages et bidouilleries ...Rien à branler2024-01-28T00:00:00+01:002024-01-28T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2024-01-28:/rien-a-branler.html<p><img alt="image" src="https://sebastien-dupire.info/ArtsBD/HumourDeBureau/20240128_fallait_pas_demander.PNG"></p>Le fantasme du Ken2024-01-24T00:00:00+01:002024-01-24T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2024-01-24:/le-fantasme-du-ken.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/fantasme_ken_1.jpg"><br>
<img alt="image" src="https://sebastien-dupire.info/drawings/fantasme_ken_2.jpg"><br>
<img alt="image" src="https://sebastien-dupire.info/drawings/fantasme_ken_3.jpg"><br>
<img alt="image" src="https://sebastien-dupire.info/drawings/fantasme_ken_4.jpg"><br>
<img alt="image" src="https://sebastien-dupire.info/drawings/fantasme_ken_5.jpg"> </p>Estimations2023-02-26T00:00:00+01:002023-02-26T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2023-02-26:/estimations.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20230226-estimations.png"> </p>Communication2023-02-25T00:00:00+01:002023-02-25T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2023-02-25:/communication.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20230225-communication.png"> </p>Ne pas confondre2023-02-24T00:00:00+01:002023-02-24T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2023-02-24:/ne-pas-confondre.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20230224-nepasconfondre.png"> </p>Une histoire de gorge2022-12-02T00:00:00+01:002022-12-02T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-12-02:/une-histoire-de-gorge.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20221202_gorge_1.jpeg"><br>
<img alt="image" src="https://sebastien-dupire.info/drawings/20221202_gorge_2.jpeg"> </p>
<p>L'article en question est <a href="https://www.lessentiel.lu/fr/story/orgasme-de-la-gorge-lavez-vous-deja-connu-918707707242">ici</a>. Je vous laisse vous faire votre propre avis.</p>Chiffrage rapide d'un projet2022-12-01T00:00:00+01:002022-12-01T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-12-01:/chiffrage-rapide-d-un-projet.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20221201-astuce-du-vendredi.png"> </p>Ventriloquie2022-12-01T00:00:00+01:002022-12-01T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-12-01:/ventriloquie.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20221201-bite-ventriloquie.png"> </p>Doctor Strange et les multivers2022-11-30T00:00:00+01:002022-11-30T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-11-30:/doctor-strange-et-les-multivers.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/doctorstrange_multi-verre.png"><br>
<img alt="image" src="https://sebastien-dupire.info/drawings/doctorstrange_multi-vert.png"><br>
<img alt="image" src="https://sebastien-dupire.info/drawings/doctorstrange_multi-ver.png"> </p>Etre au courant tout en étant branché2022-03-12T00:00:00+01:002022-03-12T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-03-12:/etre-au-courant-tout-en-etant-branche.html<p><img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/88-EP/lecon88_part1.png">
<img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/88-EP/lecon88_part2.png">
<img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/88-EP/lecon88_part3.png"></p>Quand ça suffit ...2022-01-27T00:00:00+01:002022-01-27T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-01-27:/quand-ca-suffit.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20220127_elf_1.JPG">
<img alt="image" src="https://sebastien-dupire.info/drawings/20220127_elf_2.JPG"></p>Elfe vs Orques2022-01-26T00:00:00+01:002022-01-26T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-01-26:/elfe-vs-orques.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20220126_orque1.JPG">
<img alt="image" src="https://sebastien-dupire.info/drawings/20220126_orque2.JPG">
<img alt="image" src="https://sebastien-dupire.info/drawings/20220126_orque3.JPG"></p>Sauvez Willy2022-01-25T00:00:00+01:002022-01-25T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2022-01-25:/sauvez-willy.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/20220125_SauvezWilly.PNG"></p>Se mettre au courant2021-08-04T00:00:00+02:002021-08-04T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2021-08-04:/se-mettre-au-courant.html<p><img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/87-EP/lecon87_part1.png">
<img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/87-EP/lecon87_part2.png">
<img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/87-EP/lecon87_part3.png">
<img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/87-EP/lecon87_part4.png">
<img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/87-EP/lecon87_part5.png"></p>Une vie en véhicules motorisés2021-01-31T00:00:00+01:002021-01-31T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2021-01-31:/une-vie-en-vehicules-motorises.html<p>Ca y est ! En ce mois de Janvier 2021, j'ai fait la chose la plus impensable qu'il soit : je me suis débarassé de ma MX-5, ma voiture de kéké ! Ce fût une étape importante dans ma vie de conducteur car avec cette vente s'opère un changement de mentalité de mon …</p><p>Ca y est ! En ce mois de Janvier 2021, j'ai fait la chose la plus impensable qu'il soit : je me suis débarassé de ma MX-5, ma voiture de kéké ! Ce fût une étape importante dans ma vie de conducteur car avec cette vente s'opère un changement de mentalité de mon côté. Le rapport que j'entretiens à la voiture et à la moto n'est plus le même qu'avant. Là où c'était un réel plaisir, c'est devenu maintenant un simple coût et une contrainte.</p>
<p>Je me suis dit qu'un petit récapitulatif des voitures qui ont traversées ma vie d'adulte me permettrait de faire un point.</p>
<h2>Le commencement</h2>
<p>En 1999, mes parents ont investi dans une voiture à 10 000 Francs. C'était une Ford Fiesta de 1983 MKII. A ce moment précis, elle me permettait surtout de me balader avec ma petite amie de l'époque, d'aller travailler à mon job d'été au Luxembourg et après cet été là, de descendre sur Orléans pour mes études en école d'ingénieur (ESPEO qui devint Polytech Orléans en 2003).</p>
<p><img alt="La Fiesta dans son jus" src="https://sebastien-dupire.info/pictures/vehicules-01-fiesta-arbre.jpg"></p>
<p>Elle était rigolote à conduire et très légère mais elle était bourrée de défauts : 4 vitesses, une consommation essence très élevée (autour de 11L/100km), facile à voler et pas d'autoradio. D'ailleurs, dès mon premier jour à Orléans, elle fût fracturée par des voleurs. Résultat ? L'autoradio était toujours là mais les portières pliées vers l'extérieur et une colonne de direction pétée. Mon père avait décidé de la remonter en Moselle, ce qui n'était pas plus mal car en réalité, je n'en avais pas besoin. La ville disposait de bus, l'école était à 15 minutes à pied et fin de mon cursus, il y a même eu le tramway.</p>
<p>D'ailleurs, au sujet de l'autoradio, il avait été ajouté sous le simulacre de boite à gants qui se trouvait au niveau du passager. Quand vous invitiez une fille en balade (ou un garçon), il y avait toujours ce moment gênant où vous étiez obligés de glisser votre main entre ses genoux pour pouvoir changer la fréquence de la radio ... et ce n'était pas prévu à la base ... je le jure !</p>
<p><img alt="La Fiesta et son autoradio spécial harcelement" src="https://sebastien-dupire.info/pictures/vehicules-01-fiesta-autoradio.jpg"></p>
<p>Au final, quand j'ai démarré mon stage de 2002, la voiture était nécessaire et je l'ai donc récupéré sans soucis pour en profiter pendant un an. Ensuite, le verdict tombe lors d'une changement de pneus : longerons pourris donc la voiture est irréparable. Elle est partie à la casse après seulement 17 500 kms d'utilisation.</p>
<p>Statistiques :</p>
<ul>
<li>10 000 francs à l'achat / 0 € de vente car casse. </li>
<li>17 500 kms </li>
<li>11 L / 100 km </li>
</ul>
<p>Bilan entretien dessus :</p>
<ul>
<li>vidanges classiques annuelles </li>
<li>tête de Delco changée </li>
<li>longerons troués (non réparables) </li>
</ul>
<p>Références :</p>
<ul>
<li><a href="https://fr.wikipedia.org/wiki/Ford_Fiesta">https://fr.wikipedia.org/wiki/Ford_Fiesta</a></li>
</ul>
<h2>Ma première moto pour aller travailler</h2>
<p>Suite à deal avec une amie qui partait au Brésil en 2003, j'ai récupéré sa GPZ 500 S de 1989 (frein à disque devant, tambour à l'arrière). Le deal était de la prendre pour 1 € avant son départ et de lui revendre pour 1 € à son retour, un an plus tard. Bon, quand j'ai vu l'état de la moto en la récupérant, je me suis aperçu qu'il y avait une différence de perception entre dire qu'elle est en "bon état" et voir son état réel. Elle n'était clairement pas en bon état. Il y avait de la merde dans le réservoir, la moto mettait un mal fou à démarrer, il y avait un puzzle de câble honteux qui avait été fait dans la tête de fourche ... bref ... 1 € ... Elle n'en valait pas plus et la ramener en Moselle depuis Lyon a été un parcours du combattant à cause des carburateurs encrasés qui ont fait que passé Fourvière, la moto ne voulait pas monter au dessus de 80 km/h.</p>
<p><img alt="GPZ 500 S" src="https://sebastien-dupire.info/pictures/vehicules-02-gpz-blanche-avant.jpg"></p>
<p>Après, j'ai eu beaucoup de plaisir et beaucoup d'emmerdes avec cette machine mais elle aura eu le mérite de me plonger dans la mécanique moto car j'ai passé plus de temps à la rafistoler qu'à la rouler. J'ai beaucoup appris grâce à elle. C'était mon daily aussi, notamment pour aller bosser au Luxembourg. Je me suis fait de superbes balades et je me suis fait de belles frayeurs aussi, surtout en hiver mais globalement, c'était vraiment une machine fiable et robuste. Pour ceux qui ne connaissent pas cette moto, c'est un tracteur à bas régime (merci le bi-cylindre). Elle a un creux dans les 6000 tours/min mais passé ce cap, la moto te claque dans les 11 000 tours/min et tu prends un violent coup de pied au cul malgré le fait que c'était qu'une 500 cm3. Bref, une petite moto largement suffisante pour finir au tas et s'amuser.</p>
<p>C'est en la rafistolant d'ailleurs que j'ai découvert que le travail de carrossier ne s'improvise pas. Passer des jours à poncer, pour mettre les différentes couches pour au final se vautrer sur la dernière couche de vernis et devoir tout refaire. Oueh non, ne ralez plus quand vous faites réparer votre voiture pour un petit défaut de carrosserie : cela demande un vrai savoir-faire et perso, je ne l'avais pas.</p>
<p><img alt="GPZ 500 S arrière" src="https://sebastien-dupire.info/pictures/vehicules-02-gpz-noire-peinture.jpg"></p>
<p><img alt="GPZ 500 S arrière" src="https://sebastien-dupire.info/pictures/vehicules-02-gpz-noire-avant.jpg"></p>
<p>Elle m'aura coûté environ 800 € sur une année d'exploitation mais par contre, je l'ai rendu avec 24 800 kms de plus. Autant dire que sur une année, je l'ai très bien exploitée et bichonnée. Sa propriétaire a fait une affaire en la récupérant : elle était entièrement révisée. Je m'étais dit que la prochaine, pourrait être une autre GPZ.</p>
<p>Statistiques : </p>
<ul>
<li>1 € à l'achat / 1 € à la vente</li>
<li>24 800 kms</li>
<li>11 L / 100 km</li>
</ul>
<p>Bilan entretien dessus : </p>
<ul>
<li>changement du collecteur d'échappement</li>
<li>changement du câble de compteur</li>
<li>changement du câble d'embrayage</li>
<li>changement de la roue libre du démarreur</li>
<li>changement des charbons du démarreur</li>
<li>changement du roulement de roue arrière</li>
<li>vidanges classiques annuelles</li>
<li>nettoyage et synchronisation carburateurs</li>
<li>réglage du jeu aux soupapes</li>
<li>réfection de la carrosserie + peinture</li>
</ul>
<p>Références : </p>
<ul>
<li><a href="https://fr.wikipedia.org/wiki/Kawasaki_GPZ_500">https://fr.wikipedia.org/wiki/Kawasaki_GPZ_500</a></li>
</ul>
<h2>Ma première voiture boulot</h2>
<p><img alt="205 style" src="https://sebastien-dupire.info/pictures/vehicules-03-205-avant.jpg"></p>
<p>Cette voiture, une Peugeot 205 de 1986, m'a été proposée par mon grand-père paternel. Il avait vu que j'allais au boulot en moto, il avait besoin d'une voiture moins puissante donc il me l'a proposé ... gratuitement. Je n'ai jamais vraiment su pourquoi j'y avais le droit, mais grâce à lui, j'ai pu arpenter les villes de Moselle et de Luxembourg pour bosser. Car oui, je venais de démarrer un travail comme enseignant à domicile donc, beaucoup de déplacement (comme à Grenoble pendant l'été 2004 pour une recherche d'emploi)</p>
<p><img alt="205 à grenoble" src="https://sebastien-dupire.info/pictures/vehicules-03-205-grenoble.jpg"></p>
<p>Son style était vraiment génial, un intérieur relativement confortable et j'avais même ... des bavettes dessus. Le truc qu'on ne trouve plus sur aucune voiture aujourd'hui !</p>
<p><img alt="205 avec son joli cul" src="https://sebastien-dupire.info/pictures/vehicules-03-205-arriere.jpg"></p>
<p>Elle faisait des bruits bizarres et quand je demandais à mon grand-père, il me disait : </p>
<p>"Tu as un autoradio dedans ? Ben monte le son, tu l'entendras plus le bruit". </p>
<p>J'avais récupéré la voiture avec 210 000 kms, et elle aura fini sa vie avec un deuxième moteur à 300 000 kms. Sa fin est aussi tragique que stupide. Le moteur diesel, un XUD7 consommait beaucoup trop d'eau, et le moteur était trop ancien pour changer le joint de culasse. Aucun garagiste ne voulait me le faire craignant qu'autre chose lâche dessus après. Du coup, grâce à mon beau-père de l'époque, j'ai pu acheter une autre 205 pour 500 € dont tout était mort, sauf le moteur. Et là mes amis, j'ai encore beaucoup appris en mécanique.</p>
<p><img alt="205 et sa donneuse" src="https://sebastien-dupire.info/pictures/vehicules-03-205-reparation.jpg"></p>
<p>Changer un moteur, c'est vraiment stimulant, surtout quand tu changes l'ensemble moteur + boite de vitesse, et que la boite de vitesse n'était pas la même et que tu dois trouver des solutions pour t'adapter car NON, tu ne vas pas tout enlever, juste pour une histoire de marche arrière. Je n'étais pas seul et les personnes ne seront jamais autant remercié pour l'aide apportée.</p>
<p><img alt="205 et moteur" src="https://sebastien-dupire.info/pictures/vehicules-03-205-reparation3.jpg"></p>
<p><img alt="205 et moteur" src="https://sebastien-dupire.info/pictures/vehicules-03-205-reparation4.jpg"></p>
<p><img alt="205 et moteur" src="https://sebastien-dupire.info/pictures/vehicules-03-205-reparation5.jpg"></p>
<p>Sa fin est bête car en fait, j'avais mal purgé le liquide de refroidissement du moteur quand j'avais tout remonté, et le capteur de température du ventilateur ne fonctionnait plus. Donc en plein été, coincé dans un bouchon au Luxembourg, j'ai soudain eu très très chaud dans la voiture et après avoir réussi à faire une pointe à 130 km/h sur l'A31, le moteur s'est éteint d'un coup. Le moteur était foutu. Là, je n'avais plus le temps ni l'énergie de la bricoler.</p>
<p>Bref, sa mort sur l'autoroute sera le signe d'un changement radicale. J'avais eu ma première fille un an plus tôt, et donc là, en 2007, il fallait passer à une voiture plus confortable pour le siège bébé notamment. Mais je regrette de ne pas l'avoir fait rouler plus longtemps car c'était un super karting.</p>
<p>Statistiques : </p>
<ul>
<li>0 € à l'achat / 0 € à la vente (casse)</li>
<li>90 000 kms</li>
<li>5 L / 100 km</li>
</ul>
<p>Bilan entretien dessus : </p>
<ul>
<li>vidanges classiques annuelles</li>
<li>rachat d'un autoradio suite à vol</li>
<li>changement d'une rotule de direction</li>
<li>changement d'un joint de cardan</li>
<li>changement du moteur (swap avec autre 205)</li>
<li>changement de la courroie de distribution</li>
<li>installation Hifi</li>
</ul>
<p>Références : </p>
<ul>
<li><a href="https://fr.wikipedia.org/wiki/Peugeot_205">https://fr.wikipedia.org/wiki/Peugeot_205</a></li>
<li><a href="https://fr.wikipedia.org/wiki/Moteur_XU#XUD7">https://fr.wikipedia.org/wiki/Moteur_XU#XUD7</a> </li>
</ul>
<h2>Premier bilan et suite ...</h2>
<p>C'est la fin de la première phase (2002 à 2007). J'estime avoir eu la chance de pouvoir bénéficier de véhicules comme ça à moindre coût dans une période de ma vie où l'argent n'était pas disponible et où j'en avais quand même besoin, ne serait-ce que pour postuler à des entretiens à une époque où les téléconférences n'étaient pas légion (2007, mon ADSL plafonnait à 4 Mbits/sec). Mine de rien, en dehors des grandes villes, la voiture reste essentielle pour avoir une activité professionnelle et surtout sociale. J'ai quand même fait pas loin de 132 000 kms en 5 ans dans le début de ma vie d'adulte, ce qui représente quand même environ 70 kms par jour. C'est énorme quand on y réfléchit et malheureusement, aujourd'hui, je n'ai pas réussi à réduire ce taux journalier.</p>
<p>La suite dans un prochain post ...</p>Un vibrant hommage2021-01-21T00:00:00+01:002021-01-21T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2021-01-21:/un-vibrant-hommage.html<p><img alt="image" src="https://sebastien-dupire.info/ArtsBD/Lecons/86-EP/lecon86_part1.jpg"></p>L'enfer des certificats2019-12-14T00:00:00+01:002019-12-14T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2019-12-14:/l-enfer-des-certificats.html<p>Je ne sais pas pour vous mais comme j'installe des keystores/trustores/certificats qu'une fois par projet, j'ai tendance à renseigner chaque <em>README.md</em> de chaque projet pour me rappeler les commandes principales afin de créer les clefs et les certificats nécessaires. </p>
<p>Mon cerveau ne retient jamais les commandes quand …</p><p>Je ne sais pas pour vous mais comme j'installe des keystores/trustores/certificats qu'une fois par projet, j'ai tendance à renseigner chaque <em>README.md</em> de chaque projet pour me rappeler les commandes principales afin de créer les clefs et les certificats nécessaires. </p>
<p>Mon cerveau ne retient jamais les commandes quand elles sont espacées de plusieurs mois. C'est un peu l'effet téléphone portable : on sait globalement où trouver l'information, du coup, on ne la mémorise plus.</p>
<p>Je vais tenter via cet article de faire mes derniers copier/coller sur le sujet. </p>
<h2>Utilisation de quelques commandes keytool</h2>
<p>Commençons par la création d'un keystore.</p>
<div class="highlight"><pre><span></span><code>keytool -genkey <span class="se">\</span>
-alias <mykeyalias> <span class="se">\ </span>
-keyalg RSA <span class="se">\</span>
-keysize <span class="m">4096</span> <span class="se">\ </span>
-keystore <mykeystore.extension> <span class="se">\ </span>
-dname <span class="s2">"CN=www.mydomain.dom, OU=INTERNAL, O=ETAT, L=FRANCE, ST=METZ, C=FR"</span> <span class="se">\ </span>
-ext <span class="nv">san</span><span class="o">=</span>dns:site.mydomain.dom,dns:perso.mydomain.dom <span class="se">\ </span>
-validity <span class="m">3600</span>
</code></pre></div>
<p>C'est quoi un <em>keystore</em> ? Un magasin de clef ... oui, c'est un container de clef privée/publique et de certificats. Une sorte de fourre-tout dans lequel vous pouvez associer les types d'objets cités précédemment autour d'un alias. Bien entendu, vous pouvez créer autant d'alias que vous voulez.</p>
<p>La commande ci-dessus vous permet de créer autour d'un alias nommé <mykeyalias> dans un keystore nommé <mykeystore.extension>:</p>
<ul>
<li>
<p>une paire de clef privée/publique de type RSA taille 4096,</p>
</li>
<li>
<p>un certificat x509 valide de 10 ans lié à la clef publique. </p>
</li>
</ul>
<p>Ici, je n'ai pas précisé le type de keystore voulu. Généralement, on en trouve de deux types: </p>
<ul>
<li>
<p>le <strong>JKS</strong> (extension .jks) : <a href="https://en.wikipedia.org/wiki/Java_KeyStore">Java Keystore</a>, propre à Oracle, propriétaire et qui s'utilise très bien avec le langage Java. Par contre, dans d'autres languages, c'est compliqué (je pense aussi d'un point de vue licence).</p>
</li>
<li>
<p>le <strong>PKCS12</strong> (extension .p12) : <a href="https://fr.wikipedia.org/wiki/PKCS12">Public-Key Cryptography Standards 12</a>, un format créé par les laboratoires RSA. Ils sont désormais tombés dans le domaine public.</p>
</li>
</ul>
<p>Bien entendu, il existe des outils pour convertir dans les deux formats. <a href="https://docs.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html">Keytool</a> fourni avec le JDK est le plus couramment utilisé.</p>
<p>Si à partir de notre clef privée, on veut faire un certificat signée par une <a href="https://fr.wikipedia.org/wiki/Autorit%C3%A9_de_certification">CA (Certificate Authority)</a>, quelle soit issue d'un tiers de confiance ou d'une CA interne, on peut utiliser la commande suivante pour créer une <a href="https://fr.wikipedia.org/wiki/Demande_de_signature_de_certificat">CSR (Certificate Signing Request)</a>.</p>
<div class="highlight"><pre><span></span><code>keytool -certreq <span class="se">\</span>
-alias <mykeyalias> <span class="se">\</span>
-keyalg RSA <span class="se">\</span>
–keystore <mykeystore.extension> <span class="se">\ </span>
-file /tmp/mydomain.csr
</code></pre></div>
<p>Ici, on demande à créer la CSR en allant chercher la clef privée dans l'alias <mykeyalias> du keystore <mykeystore.extension></p>
<p>Une fois envoyé à la CA de votre choix, elle va vous fournir en échange un certificat x509 signé, bien souvent au format <em>.crt</em>. Il vous suffira alors de le placer dans un nouvel alias de votre keystore.</p>
<div class="highlight"><pre><span></span><code>keytool -importcert <span class="se">\</span>
-alias <mycertificatalias> <span class="se">\</span>
-keyalg RSA <span class="se">\</span>
-keystore <mykeystore> <span class="se">\</span>
-trustcacerts <span class="se">\</span>
-file <path_to_pem_cert>
</code></pre></div>
<p>On aurait pu très bien mettre ça dans un truststore. Hein ? Quoi ? C'est quoi ? C'est juste un keystore mais qui ne contient que des certificats afin de <strong>truster</strong> des clefs publiques dans un container.</p>
<h2>Plus simple avec OpenSSL</h2>
<p>Il est possible de faire le même type d'opération avec <a href="https://www.openssl.org/">OpenSSL</a></p>
<div class="highlight"><pre><span></span><code>$ openssl <span class="se">\</span>
req <span class="se">\</span>
-nodes <span class="se">\</span>
-newkey rsa:4096 <span class="se">\</span>
-keyout mon.putain.de.site.com.key <span class="se">\</span>
-out mon.putain.de.site.com.csr <span class="se">\</span>
-subj <span class="s2">"/C=DE/ST=NRW/L=Berlin/O=My Inc/OU=DevOps/CN=mon.putain.de.site.com/emailAddress=dev@mon.putain.de.site.com"</span>
</code></pre></div>
<p>La commande vient de créer une clef privée, et la CSR qui va bien.</p>
<p>Si vous avez des sites à créer/publier et que vous voulez gérer la création de CSR et de certificats auto-signés, OpenSSL permet de passer par un fichier de configuration.</p>
<p>Commençons par créer un fichier de configuration <em>mon.putain.de.site.com.conf</em></p>
<div class="highlight"><pre><span></span><code><span class="k">[req]</span><span class="w"></span>
<span class="na">default_bits</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">4096</span><span class="w"></span>
<span class="na">default_keyfile</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">mon.putain.de.site.com.key</span><span class="w"></span>
<span class="na">distinguished_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">req_distinguished_name</span><span class="w"></span>
<span class="na">req_extensions</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">req_ext</span><span class="w"></span>
<span class="na">x509_extensions</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">v3_ca</span><span class="w"></span>
<span class="k">[req_distinguished_name]</span><span class="w"></span>
<span class="na">countryName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Country Name (2 letter code)</span><span class="w"></span>
<span class="na">countryName_default</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">FR</span><span class="w"></span>
<span class="na">stateOrProvinceName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">State or Province Name (full name)</span><span class="w"></span>
<span class="na">stateOrProvinceName_default</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">New York</span><span class="w"></span>
<span class="na">localityName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Locality Name (eg, city)</span><span class="w"></span>
<span class="na">localityName_default</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Rochester</span><span class="w"></span>
<span class="na">organizationName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Organization Name (eg, company)</span><span class="w"></span>
<span class="na">organizationName_default</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">mon.putain.de.site.com</span><span class="w"></span>
<span class="na">organizationalUnitName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">mon.putain.de.site.com</span><span class="w"></span>
<span class="na">organizationalUnitName_default</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Development</span><span class="w"></span>
<span class="na">commonName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Common Name (e.g. server FQDN or YOUR name)</span><span class="w"></span>
<span class="na">commonName_default</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">mon.putain.de.site.com</span><span class="w"></span>
<span class="na">commonName_max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">64</span><span class="w"></span>
<span class="k">[req_ext]</span><span class="w"></span>
<span class="na">subjectAltName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">@alt_names</span><span class="w"></span>
<span class="k">[v3_ca]</span><span class="w"></span>
<span class="na">subjectAltName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">@alt_names</span><span class="w"></span>
<span class="k">[alt_names]</span><span class="w"></span>
<span class="na">DNS.1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">localhost</span><span class="w"></span>
<span class="na">DNS.2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">127.0.0.1</span><span class="w"></span>
<span class="na">DNS.3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">mon.putain.de.site.com</span><span class="w"></span>
</code></pre></div>
<p>Il suffit donc d'utiliser openssl pour générer la clef privée et une CSR.</p>
<div class="highlight"><pre><span></span><code>$ openssl <span class="se">\</span>
req <span class="se">\</span>
-nodes <span class="se">\</span>
-new <span class="se">\</span>
-config mon.putain.de.site.com.conf <span class="se">\</span>
-keyout mon.putain.de.site.com.key <span class="se">\</span>
-out mon.putain.de.site.com.csr
</code></pre></div>
<p>ou alors directement pour générer la clef privée et le certificat auto-signé</p>
<div class="highlight"><pre><span></span><code>$ openssl <span class="se">\</span>
req <span class="se">\</span>
-x509 <span class="se">\</span>
-nodes <span class="se">\</span>
-days <span class="m">365</span> <span class="se">\ </span>
-newkey rsa:4096 <span class="se">\</span>
-keyout mon.putain.de.site.com.key <span class="se">\</span>
-out mon.putain.de.site.com.crt <span class="se">\</span>
-config mon.putain.de.site.com.conf
</code></pre></div>
<p>Du coup, pour configurer un nginx facilement, il n'y a plus qu'à renseigner les bonnes options</p>
<div class="highlight"><pre><span></span><code><span class="nx">server</span> <span class="p">{</span>
<span class="nx">listen</span> <span class="mf">443</span> <span class="nx">ssl</span><span class="p">;</span>
<span class="nx">server_name</span> <span class="nx">mon</span><span class="p">.</span><span class="nx">putain</span><span class="p">.</span><span class="nx">de</span><span class="p">.</span><span class="nx">site</span><span class="p">.</span><span class="nx">com</span><span class="p">;</span>
<span class="nx">ssl_certificate</span> <span class="nx">mon</span><span class="p">.</span><span class="nx">putain</span><span class="p">.</span><span class="nx">de</span><span class="p">.</span><span class="nx">site</span><span class="p">.</span><span class="nx">com</span><span class="p">.</span><span class="nx">crt</span><span class="p">;</span>
<span class="nx">ssl_certificate_key</span> <span class="nx">mon</span><span class="p">.</span><span class="nx">putain</span><span class="p">.</span><span class="nx">de</span><span class="p">.</span><span class="nx">site</span><span class="p">.</span><span class="nx">com</span><span class="p">.</span><span class="nx">key</span><span class="p">;</span>
<span class="p">...</span>
</code></pre></div>
<p>Et c'est tout. </p>
<p>Je ne détaille pas le fonctionnement des clefs privées/publiques, les certificats x509 ou les algorithmes à utiliser aujourd'hui. Cela fera l'objet d'un post à part entière. </p>
<h2>Références</h2>
<ul>
<li><a href="https://www.tbs-certificats.com/FAQ/fr/626.html">convertion PKCS12 <=> JKS</a></li>
<li><a href="https://blogs.oracle.com/blogbypuneeth/steps-to-create-a-self-signed-certificate-using-openssl">création d'un certificat self signed</a></li>
<li><a href="https://keystore-explorer.org/">l'excellent Keystore Explorer</a></li>
<li><a href="https://community.pivotal.io/s/article/Generating-a-self-signed-SSL-certificate-using-the-Java-keytool-command">encore un article sur l'utilisation de keytool...</a></li>
<li><a href="https://www.sslshopper.com/article-how-to-create-a-self-signed-certificate-using-java-keytool.html">...et encore un !</a></li>
<li><a href="https://www.sslshopper.com/article-most-common-java-keytool-keystore-commands.html">d'autres commandes</a></li>
<li><a href="https://sites.google.com/site/ddmwsst/create-your-own-certificate-and-ca">créer son propre certificat et sa CA</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-in-ubuntu-16-04">créer des certificats pour nginx</a></li>
<li><a href="https://www.humankode.com/ssl/create-a-selfsigned-certificate-for-nginx-in-5-minutes">encore du nginx</a></li>
<li><a href="https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?redirectedfrom=MSDN&view=win10-ps">ah oui, on peut aussi en faire avec powershell</a></li>
<li><a href="https://support.google.com/chrome/a/answer/7391219?hl=en">SubjectAltName - SAN - Extension et Chrome</a></li>
<li><a href="https://github.com/neard/neard/issues/308">SAN pour la validation browser</a></li>
<li><a href="https://community.spiceworks.com/topic/1987613-san-certificate-since-goolge-chrome-58-missing_subjectaltname">SAN depuis Chrome 58</a></li>
<li><a href="https://docs.oracle.com/en/database/other-databases/nosql-database/12.2.4.5/security/import-key-pair-java-keystore.html">Importer des clefs existantes dans un keystore</a></li>
</ul>Résurrection et nostalgie2019-11-13T00:00:00+01:002019-11-13T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2019-11-13:/resurrection-et-nostalgie.html<p>Ce blog est resté à l'abandon depuis début 2013. Beaucoup d'évenements pourraient expliquer cela mais je pense que c'est simplement ... la flemme. L'envie de faire vivre le blog était toujours là malgré tout mais les outils utilisés ne me convenaient plus. Et même si aux yeux de beaucoup, les blogs …</p><p>Ce blog est resté à l'abandon depuis début 2013. Beaucoup d'évenements pourraient expliquer cela mais je pense que c'est simplement ... la flemme. L'envie de faire vivre le blog était toujours là malgré tout mais les outils utilisés ne me convenaient plus. Et même si aux yeux de beaucoup, les blogs sont devenus "has been", je trouve au contraire que c'est un merveilleux outil de communication avec entre autres : </p>
<ul>
<li>flux RSS/Atom, </li>
<li>le blog, au final, c'est vraiment adapté aux articles, </li>
<li>outils et languages de syntaxe sympas pour des développeurs (mort aux WYSIWYG !!!), </li>
<li>pas de censure de la plateforme, </li>
<li>possibilité d'héberger où on veut,</li>
<li>etc ...</li>
</ul>
<p>Bref, reprenons pour un résumé de ce qu'il s'est passé depuis 2013 ! Oui, ça va être long !</p>
<h2>Résurrection du site perso</h2>
<p>J'ai utilisé <em>Pelican</em> au tout début, autour de 2012 et j'avais complétement laissé le blog à l'abandon. Je reviens sept ans plus tard et l'outil a méchamment évolué, et propose une <a href="https://docs.getpelican.com/en/stable/">documentation claire et concise</a>.</p>
<p><img alt="PelicanLeRetour" src="https://sebastien-dupire.info/pictures/pelican_come_back.jpg"></p>
<p>Il y a ce qu'il faut pour les thèmes et les plugins. La migration n'a pas été douloureuse. Le site a pu faire peau neuve en moins de 2h et du coup, même le dépot Git associé au site a été vachement épuré pour devenir enfin lisible et conforme à mon usage.</p>
<p>La seule étape longue a été de convertir mes articles existants en <em>Markdown</em>. Après, je me rends bien compte qu'ils sont pour la plupart obsolètes mais peu importe. Il suffit de lire la date de l'article pour comprendre.</p>
<p>La conversion du <em>RestructuredText</em> vers <em>Markdown</em> a été faite avec <a href="https://pandoc.org/">pandoc</a> via la <a href="https://gist.github.com/zaiste/77a946bbba73f5c4d33f3106a494e6cd">commande suivante trouvée ici</a>:</p>
<div class="highlight"><pre><span></span><code><span class="nv">FILES</span><span class="o">=</span>*.rst
<span class="k">for</span> f <span class="k">in</span> <span class="nv">$FILES</span>
<span class="k">do</span>
<span class="nv">filename</span><span class="o">=</span><span class="s2">"</span><span class="si">${</span><span class="nv">f</span><span class="p">%.*</span><span class="si">}</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"Converting </span><span class="nv">$f</span><span class="s2"> to </span><span class="nv">$filename</span><span class="s2">.md"</span>
<span class="sb">`</span>pandoc <span class="nv">$f</span> -f rst -t markdown -o <span class="nv">$filename</span>.md<span class="sb">`</span>
<span class="k">done</span>
</code></pre></div>
<p>Je suis passé à <em>Markdown</em> tout simplement car la synthaxe en <em>ReStructuredText</em> a fini par me gaver. Je ne fais quasiment plus que du <em>Markdown</em> sur l'ensemble de mes projets, donc il était temps pour moi de convertir le contenu pour éviter de garder en tête deux syntaxes proches mais au final assez différentes. </p>
<p>Le combat sera ailleurs !</p>
<h2>Refonte de mon blog BD</h2>
<p><a href="https://kyoku57.org">Mon autre site</a>, dédié à mes conneries et dessins en tout genre, est sous Wordpress (était ?). Les mises à jour succesives et sa gestion ont fini par me saouler. Des fois, aucune publication en trois mois et pourtant, obligé d'y retourner toutes les deux semaines pour faire une upgrade de Wordpress ou de PHP.</p>
<p><img alt="flingue_wordpress" src="https://sebastien-dupire.info/pictures/flinguewordpress.jpg"></p>
<p>La plupart de mes articles sont des simples textes avec une à deux images ... donc au final, je m'oriente tout doucement via la même solution que mon site perso : un générateur de site statique + Disqus. Je ne dis pas que Wordpress n'est pas bien. Bien au contraire ! C'est un outil génial, bien fait, bien pensé, configurable et <em>pluginable</em> à souhait. Il ne convient simplement plus à mon usage.</p>
<p>J'ai déjà commencé la migration via <a href="https://docs.getpelican.com/en/stable/content.html#importing-an-existing-site">la page de migration dédiée sur la documentation de Pelican</a> et le résultat a été plutôt efficace. Par contre, je suis quand même obligé de repasser sur la plupart des articles et même s'ils sont simples, il y en a moultes.</p>
<h2>Gestion des commentaires pour mes deux sites</h2>
<p><img alt="DisqusLogo" src="https://sebastien-dupire.info/pictures/disqus-logo-blue-white.png"></p>
<p>Forcément lié aux deux précédents sujets, je me suis remis dans l'usage de Disqus pour faire le ménage. Je suis heureux de constater que l'outil permet de corriger les erreurs sur les référencements et la gestion de tout ça est toujours aussi aisé. J'avais des commentaires qui étaient placés sur mon site de preview et non, mon site de production (comment ai-je fais, bordel ?). Cela a permis de tout corriger. </p>
<p>En bref, l'outil est puissant et m'a permis de migrer mon site sans casse et même de réparer des liens qui n'étaient plus bons entre certains de mes articles et les commentaires qui y étaient associés.</p>
<p>Un truc marrant en fouinant sur le site, les choses que <a href="https://disqus.com/brand/">vous ne pouvez pas faire avec le logo ou la marque Disqus</a>. </p>
<p><img alt="DisqusTrucPasCoolAFaire" src="https://sebastien-dupire.info/pictures/logo_disqus_pas_cool.jpg"></p>
<p>Niveau vie privée et tracking des utilisateurs, ce n'est pas l'idéal mais bon voilà, l'outil est gratuit ... vous connaissez la chanson, tout ça. De toute façon, même sans cookie ou autre outil, vous restez traçable. Il suffit de faire un petit tour sur <a href="https://amiunique.org/fp">https://amiunique.org/fp</a> pour finir de vous convaincre que le tracking est une affaire sérieuse.</p>
<h2>Serveur Web pour les contenus</h2>
<p>J'ai définitivement adopté <em>Nginx</em> en lieu et place d'<em>Apache2</em>. Son utilisation massive avec mes projets utilisant <em>docker</em> et <em>docker-compose</em> font que je n'utilise quasiment plus que lui. </p>
<ul>
<li>
<p>La configuration en <a href="http://nginx.org/en/docs/http/ngx_http_upstream_module.html">upstream est juste simple et élégante</a></p>
</li>
<li>
<p>La configuration du <a href="https://nginx.org/en/docs/http/configuring_https_servers.html">TLS est enfantine</a></p>
</li>
</ul>
<p>... et pour faire croire à un WebSphere que tout tourne dans un localhost, nginx dispose de trucs sympas.</p>
<p>Voici la configuration que j'ai mis en oeuvre sur un projet perso:</p>
<div class="highlight"><pre><span></span><code><span class="nx">upstream</span> <span class="nx">le</span><span class="o">-</span><span class="nx">manege</span><span class="o">-</span><span class="nx">echante</span><span class="o">-</span><span class="nx">pollux</span> <span class="p">{</span>
<span class="nx">server</span> <span class="nx">websphere</span><span class="o">-</span><span class="mf">8.5</span><span class="o">-</span><span class="nx">instance</span><span class="o">-</span><span class="nx">pollux</span><span class="o">:</span><span class="mf">9043</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">server</span> <span class="p">{</span>
<span class="nx">listen</span> <span class="mf">443</span> <span class="nx">ssl</span><span class="p">;</span>
<span class="nx">server_name</span> <span class="nx">admin</span><span class="p">.</span><span class="nx">pollux</span><span class="p">.</span><span class="nx">vm</span><span class="p">;</span>
<span class="nx">ssl_certificate</span> <span class="o">/</span><span class="nx">etc</span><span class="o">/</span><span class="nx">nginx</span><span class="o">/</span><span class="nx">ssl</span><span class="o">/</span><span class="nx">cert</span><span class="p">.</span><span class="nx">pem</span><span class="p">;</span>
<span class="nx">ssl_certificate_key</span> <span class="o">/</span><span class="nx">etc</span><span class="o">/</span><span class="nx">nginx</span><span class="o">/</span><span class="nx">ssl</span><span class="o">/</span><span class="nx">key</span><span class="p">.</span><span class="nx">pem</span><span class="p">;</span>
<span class="nx">proxy_set_header</span> <span class="nx">X</span><span class="o">-</span><span class="nx">Forwarded</span><span class="o">-</span><span class="nx">Port</span> <span class="mf">443</span><span class="p">;</span>
<span class="nx">client_max_body_size</span> <span class="mf">50</span><span class="nx">M</span><span class="p">;</span>
<span class="nx">location</span> <span class="o">/</span> <span class="p">{</span>
<span class="nx">proxy_pass</span> <span class="nx">https</span><span class="o">:</span><span class="c1">//le-manege-echante-pollux;</span>
<span class="nx">proxy_set_header</span> <span class="nx">Host</span> <span class="nx">localhost</span><span class="o">:</span><span class="mf">9043</span><span class="p">;</span>
<span class="nx">proxy_redirect</span> <span class="nx">https</span><span class="o">:</span><span class="c1">//localhost:9043 https://admin.pollux.vm;</span>
<span class="nx">sub_filter_types</span> <span class="o">*</span><span class="p">;</span>
<span class="nx">sub_filter</span> <span class="s1">'localhost:9043'</span> <span class="s1">'admin.pollux.vm'</span><span class="p">;</span>
<span class="nx">sub_filter_once</span> <span class="nx">off</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">server</span> <span class="p">{</span>
<span class="nx">listen</span> <span class="mf">80</span><span class="p">;</span>
<span class="nx">server_name</span> <span class="nx">admin</span><span class="p">.</span><span class="nx">pollux</span><span class="p">.</span><span class="nx">vm</span><span class="p">;</span>
<span class="k">return</span> <span class="mf">302</span> <span class="nx">https</span><span class="o">:</span><span class="c1">//$host$request_uri;</span>
<span class="p">}</span>
</code></pre></div>
<p>J'ai dockerisé un <em>WebSphere 8.5</em> qui expose son interface d'admin sur <em>https://localhost:9043</em>. Sauf que je veux pouvoir y accéder via https://admin.pollux.vm, un domaine fictive entré dans fichier <em>hosts</em></p>
<p>Si on utilise <em>Nginx</em> comme proxy simple, cela ne marche pas, car le routeur intégré à <em>WebSphere</em> ne comprend pas qu'on puisse arriver dessus avec un DNS et un port différents.</p>
<p>Pour gruger, je fais croire à WebSphere que je viens de <em>localhost:9043</em> via</p>
<div class="highlight"><pre><span></span><code><span class="nx">proxy_pass</span> <span class="nx">https</span><span class="o">:</span><span class="c1">//le-manege-echante-pollux;</span>
<span class="nx">proxy_set_header</span> <span class="nx">Host</span> <span class="nx">localhost</span><span class="o">:</span><span class="mf">9043</span><span class="p">;</span>
</code></pre></div>
<p>puis je lui dis de rediriger tous les <em>localhost:9043</em> vers <em>admin.pollux.vm:443</em>.</p>
<div class="highlight"><pre><span></span><code><span class="nx">proxy_redirect</span> <span class="nx">https</span><span class="o">:</span><span class="c1">//localhost:9043 https://admin.pollux.vm;</span>
</code></pre></div>
<p>Sauf qu'il reste le contenu des pages, et donc des liens dans l'interface qui pointent sur <em>localhost:9043</em> .. donc on va modifier le contenu HTML à la volée pour changer ces liens pour pointer sur le domain <em>admin.pollux.vm</em>.</p>
<div class="highlight"><pre><span></span><code><span class="nx">sub_filter_types</span> <span class="o">*</span><span class="p">;</span>
<span class="nx">sub_filter</span> <span class="s1">'localhost:9043'</span> <span class="s1">'admin.pollux.vm'</span><span class="p">;</span>
<span class="nx">sub_filter_once</span> <span class="nx">off</span><span class="p">;</span>
</code></pre></div>
<p>Magique non ? J'ai pu faire du dégueulasse temps réel pour contourner les limites d'un outil dégueulasse. </p>
<p>Le rêve, quoi !</p>
<h2>Changement et rationalisation de mes DCVS</h2>
<p>Lié au changement précédent, j'ai définitivement abandonné l'utilisation de <em>Mercurial</em> (<em>hg</em> pour les intimes). La plupart des projets sur lequels je bosse, sont sous <em>Gitlab</em> ou <em>Github</em> et mécaniquement, même si je suis resté sur <a href="https://bitbucket.org">BitBucket</a> qui proposait à l'origine que du <em>Mercurial</em>, j'ai tranquillement et sûrement adopté <em>Git</em> pour sa puissance. <em>Bitbucket</em> permettant d'héberger les projets <em>Git</em> depuis quelques années, cela a fini par m'achever et j'ai basculé dans le côté obscur de la force.</p>
<p>J'adopte le <a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow">gitflow</a> plus ou moins standard et globalement, même si je ne suis pas fan du <em>cherry-pick</em> ou du <em>rebase</em> que mes collègues plus jeunes "kiffent", je m'en tire bien.</p>
<p>Mais mieux encore, je ne fais plus du tout de <em>subversion</em> et ça ... ça n'a pas de prix !</p>
<h2>Autres outils courement utilisés</h2>
<p>Je refais une petite passe sur l'ensemble des outils que j'utilise au quotidien.</p>
<h3>Jenkins / Maven / Sonar</h3>
<p>Je n'ai pas succombé aux sirènes de <em>Gradle</em>. <em>Maven</em> reste mon outil de build principal et il fait le café.</p>
<p>Je n'ai pas succombé aux sirènes de la CI/CD de <em>Gitlab</em> car comme le dit un collègue et ami "il ne faut pas mettre tous ses oeufs dans le même panier !". Et il a raison ! Je préfère isoler les outils : gérer le code avec Gitlab, lancer les builds/tests/autres avec Jenkins.</p>
<p><img alt="ConarQube" src="https://sebastien-dupire.info/pictures/conarQube.jpg"></p>
<p>Je suis resté chez <em>Sonar</em> pour l'analyse du code car ... je vois mal un concurrent faire mieux. C'est parfaitement intégré pour Jenkins+Maven, l'interface est sympa, moulte plugins .. bref, mangez-en !</p>
<h3>Java 8 et 11 .. 13 .. etc ...</h3>
<p>Fin 2015, je finissais une longue mission qui tournait autour de Java 6. Forcément après, Java 8 a été une bouffée d'oxygène (gestion des dates, lambdas, sucres synthaxiques divers). Pour Java 11, au final, il a juste fallu être patient afin que tout le monde s'adapte à la nouvelle politique de livraison d'Oracle.</p>
<p>Pour Java 13, pour l'instant, à part pour expérimenter 2/3 bricoles, je ne vois de grands changements dans son utilisation pour moi. Le seul qui m'a intéressé, c'était pour l'écriture de bloc de texte avec le triple quotes... comme en Python ... En tout cas, je n'ai rien trouvé de bloquant à faire "à l'ancienne" pour le moment.</p>
<p>Au final, je préfère des petits changements sur la JDK tous les 6 mois, qu'une révolution tous les 5 ans et objectivement, ça va permettre à certains clients de se bouger les fesses pour maintenir leurs applications à flots surtout au niveau sécurité.</p>
<h3>Docker et docker-compose</h3>
<p>Je ne peux plus m'en passer. </p>
<ul>
<li>J'ai besoin d'une base MySQL ? hop ... docker</li>
<li>J'ai besoin d'un sonar ? hop ... dockker</li>
<li>J'ai besoin d'un SMTP de type MailHog ? hop ... docker</li>
<li>J'ai trop de docker à gérer ? hop ... docker-compose</li>
</ul>
<p>Je ne vais pas m'étaler sur les bienfaits des containers. Contentez vous d'en bouffer, votre vie de développeur va devenir plus agréable.</p>
<h3>Vagrant</h3>
<p>Alors troll ou pas ? Ben trop pas ! J'adore l'idée d'installer toutes les merdes liées à un projet dans une VM isolée du reste. Pour peu que vous utilisiez les <a href="https://app.vagrantup.com/bento">images bento</a>, vous avez par défaut un répertoire partagée avec le host qui vous permet de rester sur votre host tout en lançant les commandes de build & deploy sur votre VM.</p>
<p>Le seul hic, c'est le problème avec les projets NodeJS bourés de liens symboliques dans le <em>node_modules</em> qui font que si vous compilez votre application dans ce répertoire partagé et que vous avez le malheur d'avoir un <em>host</em> sous Windows, alors ça va méchamment partir en cacahouète. Vous êtes prévenus ! Mais ... à priori, <a href="https://vomtom.at/running-vagrant-with-symbolic-links-enabled/">il y a une solution</a>.</p>
<h3>IntelliJ et Visual Code</h3>
<p>Depuis 2012 sous Eclipse qui, pour moi, est vraiment un très bon IDE. Les dernières versions sont rapides, possèdent un Dark thème et globalement, tout fonctionne très bien dessus. Seulement dans le cadre d'un projet, j'ai du changer d'IDE et bon, IntelliJ a fini par me convaincre. Les options, les configurations plus rapides ... même si je n'ai pas encore bien appris les raccourcis clavier, au final, je le trouve plus pratique à utiliser au quotidien.</p>
<p><a href="https://code.visualstudio.com/">Visual Code</a> est par contre, la claque que je n'attendais pas. Que ce soit pour les projets NodeJS, Go ou Python, ou même pour l'édition de ce blog en <em>Markdown</em>, je ne peux plus m'en passer. Il se lance rapidement, il est bourré d'extensions, tout est bien disposé, utile, bien pensé et en Python, l'autocomplétion est au top, la gestion de Git fonctionne très bien. Bref, fini Notepad++ et la plupart des IDE que j'utilisais par le passé. J'ai grandement adopté cet outil. Mes plugins préférés dessus ? Je ne vais en citer que deux.</p>
<ul>
<li>
<p>Utilisation de <a href="https://mermaidjs.github.io/#/">MermaidJS</a> pour faire des diagrammes de séquences notamment.</p>
</li>
<li>
<p>Plugin pour faire du Base64 encode / decode pour simplement décoder des retours de requêtes SAML avec une simple option dans l'IDE. C'est magique.</p>
</li>
</ul>
<p>Il ne manquerait plus que je fasse du Java avec ... (en vrai, j'ai essayé, mais je ne suis absolument pas convaincu ou alors, j'ai très mal configuré le bouzin).</p>
<h3>Gitbash (et les extensions)</h3>
<p>La plupart du temps, mon bash de prédilection sous Windows est <a href="https://www.atlassian.com/git/tutorials/git-bash">Gitbash</a> qui est fourni avec la version <a href="https://gitforwindows.org/">Git for Windows</a>. Alors oui, beaucoup critique ce choix, mais il fait le café, et il est possible d'y installer des extensions pour gérer l'ensemble des commandes GNU/Linux.</p>
<p><img alt="GitBash" src="https://sebastien-dupire.info/pictures/gitbash.jpg"></p>
<h3>Système d'exploitation</h3>
<p>Oui, je suis majoritairement sous <em>Windows 10</em> car pour la plupart des environnements où je suis, cela est nécessaire et pour avoir accès à la suite office, ben cela fonctionne mieux aussi. C'est con, mais l'intégration de la lecture des cartes à puces, et d'autres périphériques folkloriques ne fonctionnent bien souvent que sous Windows.</p>
<p>De plus, désormais, on a un bash Ubuntu disponible, il est possible de virtualiser des environnements avec <em>vagrant</em> voir <em>Docker for Windows</em> si vous aimez vous prendre la tête ... et on a des bureaux virtuels aussi maintenant. Objectivement, <em>Windows</em> n'est pas un problème au quotidien, même si après des années sous GNU/Linux, ça m'arrache un peu la gueule de le dire.</p>
<p><img alt="sousWindows" src="https://sebastien-dupire.info/pictures/windowscreve.jpg"></p>
<h2>Humainement parlant ?</h2>
<p>Beaucoup trop choses pour être résumé sur un article, mais en vrac :</p>
<ul>
<li>
<p>Depuis 2/3 ans, je m'intéresse à tout ce qui est lié à sécurité. L'organisation OWASP, et notamment les initiatives sur notamment la définition des cas d'abus plutôt que d'appliquer une checklist stupide. C'est un boulot à plein temps. Malgré le temps passé dessus, je pense que je suis simplement "sensibilisé" à ce domaine.</p>
</li>
<li>
<p>Mon profil s'est spécialisé aussi dans tout ce qui est gestion de certificats, signature électronique, et architecture même si j'avoue ne pas avoir encore basculé dans tout ce qui tourne autour de k8s et le monde merveilleux des microservices...pas encore. </p>
</li>
<li>
<p>L'arrivée de petits jeunes dans la boite où je bosse fait un bien fou. Ils sont fougueux, plein de riches idées, souvent nouvelles et du coup, c'est un véritable plaisir d'apprendre autant de choses à leurs côtés. D'ailleurs, il y a une forme d'ambiguité. Ils viennent me voir car on me qualifie d'expert mais bien souvent ... je suis une quiche sur les sujets sur lesquels on me sollicite. Je commence à être catégorisé comme un "pompier du dev" pour aller là où les autres se chient dessus. N'allez pas croire ! Je me chie dessus aussi mais de manière professionnelle et bizarrement, je m'en sors toujours pas trop mal !</p>
</li>
<li>
<p>J'ai quitté le monde Struts 2/JEE pour faire principalement du SpringBoot 2 et Angular 6/7/8. J'ai pu faire un peu de Go, faire un petit retour sur Python temporaire, bref, ça fait du bien. Pour le coup, même une petite dérive sur PHP ne m'a pas déplu. Ce n'est pas que je n'aime pas Java.</p>
</li>
<li>
<p>Les YAJUG (dont le <a href="https://yajug.lu/program/">programme est ici</a>)... comment ne pas les citer ? Si vous voulez assister à des conférences diverses et variées (non, ce n'est pas uniquement lié à Java), il faut y aller.</p>
</li>
<li>
<p>J'en oublie sûrement ...</p>
</li>
</ul>
<h2>La suite ?</h2>
<p><img alt="AladdinEtSesTroisVoeux" src="https://sebastien-dupire.info/pictures/lampealaddin.jpg"></p>
<ul>
<li>
<p>J'aimerais finir mes tutoriels sur l'utilisation de canvas parce que c'est rigolo, et surtout quand on abordera l'utilisation de framework tout en un.</p>
</li>
<li>
<p>J'aimerais faire une serie de tutoriels très enfantins sur la création d'une API REST sécurisée mais avec différents langages et frameworks (Python-flask, Django Rest, Go, NodeJS notamment via Express, JavaEE avec les librairies standards, SpringBoot). Il me faut juste définir un cahier des charges simple et après le but serait de toutes les bourriner avec un test de charge pour évaluer les performances de chaque implémentation.</p>
</li>
<li>
<p>J'aimerais faire à nouveau un peu de mobile notamment pour utiliser tous les périphériques, apprendre à faire des interfaces utilisateurs cools, me connecter à tous types de backend, tout ça dans un mouchoir de poche.</p>
</li>
</ul>
<h2>Divers</h2>
<p>Je termine par faire le point sur ma sélection de quelques lectures/visionnages du mois.</p>
<ul>
<li>
<p><a href="https://linuxfr.org/users/ploum/journaux/linuxfr-en-j2ee">Une vieille histoire de site Web sous J2EE par Ploum</a> : Toujours aussi marrant à lire et étant consultant moi-même sur Java/JEE, j'en retrouve certains travers. Par contre, je ne joue pas sur mon mobile.</p>
</li>
<li>
<p><a href="https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Abuse_Case_Cheat_Sheet.md">OWASP Abuse Case Cheat Sheet</a> : une nouvelle façon d'envisager la sécurité dans les développements en se concentrant sur les cas d'abus technique et fonctionnel plutôt que d'appliquer des checks lists à tout va ...donc une manière de faire plus pragmatique.</p>
</li>
<li>
<p>D'ailleurs, toujours dans le même thème, si vous voulez vous sensibiliser à tous ces points, je vous invite à vous tournez vers <a href="https://github.com/WebGoat/WebGoat">WebGoat</a> (un peu vieux et certains tests sont justes mal documentés), <a href="https://www2.owasp.org/www-project-juice-shop/">Juice Shop</a>, <a href="https://github.com/OWASP/NodeGoat">NodeGoat</a> ou encore le toujours en place <a href="https://www.root-me.org/">Root Me</a>.</p>
</li>
<li>
<p>WSO2 a mis récemment à jour son <a href="https://wso2.com/api-management/">API Manager</a> et son <a href="https://wso2.com/identity-and-access-management/">Identity Server</a> .. en fait, je m'en fous, mais comme j'ai bossé avec ce produit, j'y ai quand même jeté un coup d'oeil, et par rapport aux précédentes versions, ils ont abandonné leurs frameworks tout bizarre pour au final faire du ReactJS. Comme quoi ... la concurrence des autres produits ont du les faire réagir.</p>
</li>
<li>
<p>Septembre 2019, c'était la <a href="https://openjdk.java.net/projects/jdk/13/">sortie du JDK 13</a> aussi.</p>
</li>
<li>
<p>La possibilité de pouvoir faire des <a href="https://netapp.io/2017/06/22/snapshots-clones-docker-volume-paradigm/">snapshots de volume Docker</a> m'intéresse mais à priori, ce n'est pas dans la version de Docker, ni dans le pipe. D'un côté, ce serait tellement le bordel à gérer.</p>
</li>
<li>
<p>A priori <a href="https://openclassrooms.com/fr/courses/4902061-developpez-une-application-mobile-react-native">ce tutoriel est cool</a> pour faire des applications mobiles avec React Native. Il ne me reste plus qu'à me trouver un créneau pour jouer avec ça.</p>
</li>
<li>
<p>Mais du coup, il y a aussi <a href="https://flutter.dev/">Flutter</a> qui a débarqué et qui semble fournir tous les outils pour faire des superbes applications mobiles pour les feignasses dans mon genre.</p>
</li>
<li>
<p>Alors ça fait jeune débutant mais oui, je me suis mis très tard à Git, et j'ai appris l'existance du <a href="https://git-scm.com/book/fr/v1/Utilitaires-Git-Le-remisage">remissage</a>. C'est cool !</p>
</li>
</ul>Y'a plus qu'à, Pika !2019-10-23T00:00:00+02:002019-10-23T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2019-10-23:/ya-plus-qua-pika.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/pikalaurine.jpg"></p>Une histoire de ton et d'humeur2017-07-11T13:30:00+02:002017-07-11T13:30:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2017-07-11:/une-histoire-de-ton-et-dhumeur.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/drawings/laurine_triste.jpg"></p>Deuxième bobo2017-06-05T00:00:00+02:002017-06-05T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2017-06-05:/deuxieme-bobo.html<p>Ma grande fille a commencé sérieusement le dessin, il y a environ deux ans. Gravures, peintures acryliques, compositions. Je ne peux pas sans autorisation de sa part, diffuser ses oeuvres actuels (faut dire que je ne lui ai jamais demandé ...). Par contre, pour celui-ci, j'ai eu son autorisation et je …</p><p>Ma grande fille a commencé sérieusement le dessin, il y a environ deux ans. Gravures, peintures acryliques, compositions. Je ne peux pas sans autorisation de sa part, diffuser ses oeuvres actuels (faut dire que je ne lui ai jamais demandé ...). Par contre, pour celui-ci, j'ai eu son autorisation et je l'archive pour la posterité et par fierté de voir son évolution de dessinatrice au fur et à mesure.</p>
<p><img alt="bobo_elora" class="aligncenter" src="https://sebastien-dupire.info/drawings/deuxieme_bobo_petit.jpg"></p>
<p>Ce dessin représente le jour où je l'ai mise sur mes épaules en oubliant la hauteur que j'avais au total et qui ne me permettait pas de passer sous un panneau IKEA ! Père indigne ! (Que le parent à qui ce genre de conneries n'arrivent jamais me jete la première pierre).</p>
<p>Ma grande a fortement progressé ces dernières années aussi bien en version papier qu'avec sa tablette graphique. Elle m'impressionne à chaque nouveau dessin. Elle s'est aussi mise à <a href="https://krita.org/fr/">Krita</a> pour faire de l'<a href="https://www.youtube.com/watch?v=1xFtVJtaXKc">animation 2D</a>. Je recommande fortement cet outil Open Source que je trouve plus adapté au dessin que ne peut l'être <a href="https://www.gimp.org/fr/">The Gimp</a>. Si vous avez l'occasion, le tutoriel sur l'animation d'une balle est rapide à comprendre et à réaliser.</p>La révolte du poil2017-01-17T00:00:00+01:002017-01-17T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2017-01-17:/la-revolte-du-poil.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/drawings/Novembre2016-SDEBarbe.jpg"></p>Le dernier arrivé2017-01-17T00:00:00+01:002017-01-17T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2017-01-17:/le-dernier-arrive.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/drawings/Septembre2016-UnNouveauPtitMec.jpg"></p>
<p>J'adore les gosses et j'ai toujours voulu en avoir. Après ma fille en 2006, cela faisait 10 ans que j'attendais un nouveau mioche :</p>
<ul>
<li>Je ne me rappelais plus que ça gueulait autant</li>
<li>Flûte ! Les garçons, c'est pas pareil. Faut tout réapprendre.</li>
<li>Avec l'allaitement, le caca, ça ne sent quasiment pas …</li></ul><p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/drawings/Septembre2016-UnNouveauPtitMec.jpg"></p>
<p>J'adore les gosses et j'ai toujours voulu en avoir. Après ma fille en 2006, cela faisait 10 ans que j'attendais un nouveau mioche :</p>
<ul>
<li>Je ne me rappelais plus que ça gueulait autant</li>
<li>Flûte ! Les garçons, c'est pas pareil. Faut tout réapprendre.</li>
<li>Avec l'allaitement, le caca, ça ne sent quasiment pas</li>
<li>Finalement, c'est comme le vélo, on retrouve ses réflexes de papa</li>
<li>Les nuits courtes ... j'avais oublié...</li>
</ul>Agile night .. troisième round2017-01-16T00:00:00+01:002017-01-16T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2017-01-16:/agile-night-troisieme-round.html<p><img alt="agilenight" class="aligncenter" src="https://sebastien-dupire.info/drawings/agilenight.jpg"></p>Un remake français ...2017-01-11T00:00:00+01:002017-01-11T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2017-01-11:/un-remake-francais.html<p><img alt="train_luxembourg" class="aligncenter" src="https://sebastien-dupire.info/drawings/train_luxembourg.jpg"></p>
<p>Pour la petite histoire, prendre le train pour aller au Luxembourg est une vraie aventure quotidienne tellement il y a de merde sur l'axe Moselle-Luxembourg. C'est ce qui fait qu'en 2015, j'ai finalement arrêté de le prendre préférant perdre mon temps en voiture. C'est nul, ça coûte plus cher, mais …</p><p><img alt="train_luxembourg" class="aligncenter" src="https://sebastien-dupire.info/drawings/train_luxembourg.jpg"></p>
<p>Pour la petite histoire, prendre le train pour aller au Luxembourg est une vraie aventure quotidienne tellement il y a de merde sur l'axe Moselle-Luxembourg. C'est ce qui fait qu'en 2015, j'ai finalement arrêté de le prendre préférant perdre mon temps en voiture. C'est nul, ça coûte plus cher, mais au moins c'est quasi-régulier et je suis dans mon confort entre le chauffage en hiver, la climatisation en été. Et ne plus être serré comme une sardine à sentir les aisselles de son voisin, ça n'a pas de prix aussi.</p>Les Chevaliers d'Ethereum2016-12-15T15:02:00+01:002016-12-15T15:02:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2016-12-15:/les-chevaliers-dethereum.html<p><img alt="nebulaire" class="aligncenter" src="https://sebastien-dupire.info/drawings/nebulaire_p.jpg"></p>Végétaliniens2016-12-15T00:00:00+01:002016-12-15T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2016-12-15:/vegetaliniens.html<p><img alt="crepes_01" class="aligncenter" src="https://sebastien-dupire.info/drawings/vegetaliniens.jpg"></p>Une pensée pour mon petit chaton2016-12-07T13:06:00+01:002016-12-07T13:06:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2016-12-07:/une-pensee-pour-mon-petit-chaton.html<p><img alt="couille" class="aligncenter" src="https://sebastien-dupire.info/drawings/couille.jpg"></p>Agile knights2016-12-06T00:00:00+01:002016-12-06T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2016-12-06:/agile-knights.html<p><img alt="peggy" class="aligncenter" src="https://sebastien-dupire.info/drawings/peggy.jpg"></p>GabZilla2016-11-26T00:00:00+01:002016-11-26T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2016-11-26:/gabzilla.html<p><img alt="gabzilla" class="aligncenter" src="https://sebastien-dupire.info/drawings/gazilla.jpg"></p>the Walking parents2016-10-16T00:00:00+02:002016-10-16T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2016-10-16:/the-walking-parents.html<p><img alt="walking_parents" class="aligncenter" src="https://sebastien-dupire.info/drawings/walking_parents.jpg"></p>Un dur retour2016-10-15T00:00:00+02:002016-10-15T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2016-10-15:/un-dur-retour.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/drawings/retour_anne_lise.jpg"></p>Gabriel est arrivé2016-09-21T00:00:00+02:002016-09-21T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2016-09-21:/gabriel-est-arrive.html<p><img alt="cignogne" class="aligncenter" src="https://sebastien-dupire.info/drawings/cignogne.jpg"></p>Une technique infaillible2016-07-27T00:00:00+02:002016-07-27T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2016-07-27:/une-technique-infaillible.html<p><img alt="carine2016" class="aligncenter" src="https://sebastien-dupire.info/drawings/carine2016.jpg"></p>Crêpes party2016-02-11T00:00:00+01:002016-02-11T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2016-02-11:/crepes-party.html<p><img alt="crepes_01" class="aligncenter" src="https://sebastien-dupire.info/drawings/crepes_01.jpg"></p>
<p><img alt="crepes_02" class="aligncenter" src="https://sebastien-dupire.info/drawings/crepes_02.jpg"></p>
<p><img alt="crepes_03" class="aligncenter" src="https://sebastien-dupire.info/drawings/crepes_03.jpg"></p>
<p><img alt="crepes_04" class="aligncenter" src="https://sebastien-dupire.info/drawings/crepes_04.jpg"></p>
<p><img alt="crepes_05" class="aligncenter" src="https://sebastien-dupire.info/drawings/crepes_05.jpg"></p>
<p><img alt="crepes_06" class="aligncenter" src="https://sebastien-dupire.info/drawings/crepes_06.jpg"></p>
<p><img alt="crepes_07" class="aligncenter" src="https://sebastien-dupire.info/drawings/crepes_07.jpg"></p>Dans mon bain2015-11-29T00:00:00+01:002015-11-29T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2015-11-29:/dans-mon-bain.html<p><img alt="candice" class="aligncenter" src="https://sebastien-dupire.info/pictures/bain.jpg"></p>Spider Cochon2015-08-06T00:00:00+02:002015-08-06T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2015-08-06:/spider-cochon.html<p><img alt="candice" class="aligncenter" src="https://sebastien-dupire.info/drawings/candice.jpg"></p>l'âge de l'innovation2015-07-27T00:00:00+02:002015-07-27T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2015-07-27:/lage-de-linnovation.html<p><img alt="carine" class="aligncenter" src="https://sebastien-dupire.info/drawings/carine_carbonic.jpg"></p>Superman Reboot Return2013-07-14T00:00:00+02:002013-07-14T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2013-07-14:/superman-reboot-return.html<p><img alt="image" src="https://sebastien-dupire.info/drawings/superman.jpg">
<img alt="image" src="https://sebastien-dupire.info/drawings/superman2.jpg"></p>Jouer avec canvas - épisode 22013-02-19T00:00:00+01:002013-02-19T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2013-02-19:/javascript-jouer-avec-canvas-episode-2.html<p>L'an dernier, j'avais rédigé un billet <em>Quick & Dirty</em> sur l'animation d'une image/sprite dans l'élement canvas. Il s'agissait d'un petit avion qui se déplaçait dans un bête rectangle. La programmation n'était pas vraiment structurée afin de suivre la logique de mise en oeuvre rapide et de montrer des principes simples …</p><p>L'an dernier, j'avais rédigé un billet <em>Quick & Dirty</em> sur l'animation d'une image/sprite dans l'élement canvas. Il s'agissait d'un petit avion qui se déplaçait dans un bête rectangle. La programmation n'était pas vraiment structurée afin de suivre la logique de mise en oeuvre rapide et de montrer des principes simples d'animations.</p>
<p>Pour ce deuxième billet, le principe sera la même : nous allons reprendre le code précédent et le modifier afin d'y inclure l'animation d'un fond sans trop se préoccuper de l'évolution du code
et de sa structure optimale. Nous resterons en <em>Quick & Dirty</em>.</p>
<h2>Premiers travaux après un an</h2>
<p>Le code repris, il est nécessaire de faire un peu de ménage :</p>
<ul>
<li>certaines méthodes sont en franglais (méthode <a href="http://www.risacher.com/la-rache/">La Rache</a>)</li>
<li>certaines méthodes sont en <a href="http://en.wikipedia.org/wiki/CamelCase">CamelCase</a> d'autres avec des underscores (selon l'humeur ?)</li>
<li>certains traitements ne sont pas isolés et on retrouve trop de choses au sein d'une même fonction</li>
</ul>
<p>Le but n'est pas de fournir une structure organisée (cela fera l'objet d'un billet spécial refactoring). Par contre, ça ne coûte pas grand chose de nettoyer un peu.</p>
<p><strong>Première chose</strong> : nous allons faire en sorte que notre avion ne sorte plus du canvas.</p>
<p>La méthode qui s'occupait de calculer les positions de l'avion était la suivante</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">calculPositions</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">debug_gap</span><span class="p">();</span>
<span class="cm">/* Calcul des nouvelles coordonées */</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goUp</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goDown</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goLeft</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goRight</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* Calcul des turbulences */</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">turbo</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">rI</span> <span class="o"><</span> <span class="nx">rIMax</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">rI</span> <span class="o">=</span> <span class="nx">rI</span> <span class="o">+</span> <span class="nx">rIAcc</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">rI</span> <span class="o">></span> <span class="mf">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">rI</span> <span class="o">=</span> <span class="nx">rI</span> <span class="o">-</span> <span class="nx">rIAcc</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">rX</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="nx">rI</span><span class="p">)</span><span class="o">-</span><span class="nx">rI</span><span class="o">/</span><span class="mf">2.0</span><span class="p">);</span>
<span class="nx">rY</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="nx">rI</span><span class="p">)</span><span class="o">-</span><span class="nx">rI</span><span class="o">/</span><span class="mf">2.0</span><span class="p">);</span>
<span class="cm">/* Opération bidon */</span>
<span class="cm">/* for(var i=0; i<400000; i++) {} */</span>
<span class="cm">/* Calcul du temps d'execution */</span>
<span class="nx">exec_time</span> <span class="o">=</span> <span class="nx">debug_gap</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>
<p>Après nettoyage, nous changeons de nom et ajoutons le contrôle sur la position vis à vis du canvas. Rien de plus simple.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">computePlanePosition</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="cm">/* Calcul des nouvelles coordonées */</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goUp</span> <span class="o">&&</span> <span class="nx">posY</span> <span class="o">></span> <span class="o">-</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">height</span> <span class="o">/</span> <span class="mf">2</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goDown</span> <span class="o">&&</span> <span class="nx">posY</span> <span class="o"><</span> <span class="nx">canvasHeight</span> <span class="o">-</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">height</span> <span class="o">/</span> <span class="mf">2</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goLeft</span> <span class="o">&&</span> <span class="nx">posX</span> <span class="o">></span> <span class="o">-</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">width</span> <span class="o">/</span> <span class="mf">2</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goRight</span> <span class="o">&&</span> <span class="nx">posX</span> <span class="o"><</span> <span class="nx">canvasWidth</span> <span class="o">-</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">width</span> <span class="o">/</span> <span class="mf">2</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* Turbulences */</span>
<span class="nx">rX</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="nx">rI</span><span class="p">)</span><span class="o">-</span><span class="nx">rI</span><span class="o">/</span><span class="mf">2.0</span><span class="p">);</span>
<span class="nx">rY</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="nx">rI</span><span class="p">)</span><span class="o">-</span><span class="nx">rI</span><span class="o">/</span><span class="mf">2.0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Nous en profitons pour retirer tout ce qui était lié au calcul des accélérations pour le mettre dans une méthode séparée</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">computeAcceleration</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">turbo</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">rI</span> <span class="o"><</span> <span class="nx">rIMax</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">rI</span> <span class="o">=</span> <span class="nx">rI</span> <span class="o">+</span> <span class="nx">rIAcc</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">rI</span> <span class="o">></span> <span class="mf">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">rI</span> <span class="o">=</span> <span class="nx">rI</span> <span class="o">-</span> <span class="nx">rIAcc</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Comme cela, si un autre élément dépend du mode <strong>turbo</strong>, il suffira d'inclure la valeur de <strong>rI</strong> calculée.</p>
<p><strong>Deuxième étape</strong> : séparer le rendu de l'élement avion de la méthode de rendu générique</p>
<p>La méthode de rendu générique devient :</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderFrame</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">frame_count</span><span class="o">++</span><span class="p">;</span>
<span class="nx">countGap</span><span class="p">();</span>
<span class="cm">/* On vérouille la frame */</span>
<span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="cm">/* Dessin de la nouvelle Frame */</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="s2">"rgb(220, 220, 220)"</span><span class="p">;</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="cm">/* Différents rendus */</span>
<span class="nx">renderPlane</span><span class="p">();</span>
<span class="cm">/* Dévérouillage de la frame */</span>
<span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="cm">/* Calcul du temps de rendu */</span>
<span class="nx">render_time</span> <span class="o">=</span> <span class="nx">countGap</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>
<p>avec la nouvelle méthode <strong>renderPlane</strong></p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderPlane</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">plane</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
<span class="nx">posX</span><span class="o">+</span><span class="nx">rX</span><span class="p">,</span> <span class="nx">posY</span><span class="o">+</span><span class="nx">rY</span><span class="p">,</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">plane</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p><strong>Troisième étape</strong> : préparer le fait que plusieurs ressources devront être chargées :</p>
<p>Notre avion ne sera pas à terme, le seul élement à charger dans notre canvas. Du coup, le pré-loading des images va légerement changer pour devenir ceci.</p>
<div class="highlight"><pre><span></span><code><span class="nx">$</span><span class="p">(</span><span class="s2">"#precharge"</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">hide</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">nb_of_ressources</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">nb_of_ressources_loaded</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
<span class="nx">nb_of_ressources</span><span class="o">++</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">plane</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Image</span><span class="p">();</span>
<span class="nx">plane</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">"plane_200_95_1.png"</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">plane</span><span class="p">).</span><span class="nx">load</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">nb_of_ressources_loaded</span><span class="o">++</span><span class="p">;</span>
<span class="nx">updateRessourcesLoading</span><span class="p">();</span>
<span class="p">});</span>
<span class="kd">var</span> <span class="nx">updateRessourcesLoading</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">nb_of_ressources_loaded</span> <span class="o">==</span> <span class="nx">nb_of_ressources</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#precharge"</span><span class="p">).</span><span class="nx">hide</span><span class="p">();</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#precharge"</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">hide</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Tout ceci pourra aussi être structuré de manière plus propre, personne
n'en doute. Si nous rajoutons des ressources, il faudra rajouter un
block concernant la nouvelle ressource et incrémenter
<strong>nb_of_ressources</strong>.</p>
<p>En gros, on a la structure suivante :</p>
<ul>
<li>des variable pour les états des différents élements (position, état de l'accélération, etc ...)</li>
<li>une partie dédiée au préchargement des ressources utilisées par le canvas</li>
<li>Une méthode de calcul des accélérations</li>
<li>Une méthode de calcul des positions</li>
<li>Une méthode de rendu de l'avion</li>
<li>Une méthode de rendu général</li>
<li>Une boucle principale d'animation</li>
</ul>
<p>L'application va lancer une boucle infinie qui calcule les accélérations nécessaires pour l'ensemble des élements, les positions des élements, puis créer le rendu pour l'affichage. La contrainte est toujours que l'ensemble des deux opérations se fassent en moins de 25 millisecondes, le rendu étant "facultatif". Les positions seront systématiquement calculées, ce qui ne sera pas le cas des images en cas de latence. Ce choix implique qu'en cas de lag, l'image "saute" des frames.</p>
<h2>Le fond défile</h2>
<p>Dans notre exemple, nous voulons faire en sorte que notre fond défile de droite à gauche pour simuler le déplacement de notre petit avion.</p>
<p><img alt="Le canvas et son background" src="https://sebastien-dupire.info/pictures/0009-canvas-base.jpg"></p>
<p>Le but sera de déplacer le fond sur la gauche et de gérer la façon d'enchainer la suite du décor à partir du même fond. Il est donc nécessaire de faire en sorte que la gauche de notre image et sa droite sont raccords.</p>
<p><img alt="notre fond raccord des deux côtés" src="https://sebastien-dupire.info/pictures/0009-background1.jpg"></p>
<p>Notre fond sera plus grand que le canvas qui fait office de fenêtre sur le décor utilisé. Ici, comme pour l'avion, nous allons charger les ressources avant toute opération sur le canvas.</p>
<div class="highlight"><pre><span></span><code><span class="nx">nb_of_ressources</span><span class="o">++</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">background1</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Image</span><span class="p">();</span>
<span class="nx">background1</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">"background1.png"</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">background1</span><span class="p">).</span><span class="nx">load</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">nb_of_ressources_loaded</span><span class="o">++</span><span class="p">;</span>
<span class="nx">updateRessourcesLoading</span><span class="p">();</span>
<span class="p">});</span>
</code></pre></div>
<p>Ensuite, nous définissons une variable pour la position de notre fond sur l'axe X</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">background1_posX</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
</code></pre></div>
<p>Puis, nous pouvons mettre en place le calcul de la position du background</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">computeBackgroundsPositions</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">background1_posX</span> <span class="o">-=</span> <span class="mf">4</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Enfin, nous nous occupons de son rendu</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderBackgrounds</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// trame principal</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">background1</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="o">-</span><span class="nx">canvasHeight</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
<span class="nx">background1_posX</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Bien sûr, on ajoute tout ceci à la méthode de rendu principale, en lançant <strong>renderBackgrounds</strong> avant <strong>renderPlane</strong>.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderFrame</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">frame_count</span><span class="o">++</span><span class="p">;</span>
<span class="nx">countGap</span><span class="p">();</span>
<span class="cm">/* On vérouille la frame */</span>
<span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="cm">/* Dessin de la nouvelle Frame */</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="s2">"rgb(220, 220, 220)"</span><span class="p">;</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="cm">/* Différents rendus */</span>
<span class="nx">renderBackgrounds</span><span class="p">();</span>
<span class="nx">renderPlane</span><span class="p">();</span>
<span class="cm">/* Dévérouillage de la frame */</span>
<span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="cm">/* Calcul du temps de rendu */</span>
<span class="nx">render_time</span> <span class="o">=</span> <span class="nx">countGap</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>
<p>Au lancement de l'animation, pas vraiment de surprises : le décor défile lentement et disparait sur notre gauche pour ne plus revenir.</p>
<p><img alt="Le fond qui s'arrête" src="https://sebastien-dupire.info/pictures/0009-error-end.gif"></p>
<p>Pour corriger cette disparition, nous allons déjà faire en sorte que dès que le fond entier est passé, il revienne à sa position de départ.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">computeBackgroundsPositions</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">background1_posX</span> <span class="o">-=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="mf">4</span> <span class="o">+</span> <span class="nx">rI</span><span class="p">);</span>
<span class="nx">background1_posX</span> <span class="o">=</span> <span class="nx">background1_posX</span> <span class="o">%</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Grâce à ça, nous obtenons ce que j'appellerais, le "reste à afficher". S'il n'y a plus rien à afficher, c'est qu'il faut tout réafficher car nous avons fait une boucle.</p>
<p>Voilà le résultat :</p>
<p><img alt="Le fond qui saute" src="https://sebastien-dupire.info/pictures/0009-error-boucle.gif"></p>
<p>Le soucis est que nous avons toujours un blanc à combler lorsque le "reste à afficher" est plus petit que la longueur du canvas. Il faut donc modifier la fonction de rendu afin d'afficher le complément provenant de la même image mais décalé après le "reste à afficher".</p>
<p><img alt="La boucle du fond" src="https://sebastien-dupire.info/pictures/0009-canvas-raccord.jpg"></p>
<p>Nous pouvons réaliser ceci avec la modification suivante:</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderBackgrounds</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// trame principal</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">background1</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="o">-</span><span class="nx">canvasHeight</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
<span class="nx">background1_posX</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
<span class="c1">// position de la copie décalée</span>
<span class="kd">var</span> <span class="nx">size_of_visible</span> <span class="o">=</span> <span class="nx">background1_posX</span> <span class="o">+</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">size_of_visible</span> <span class="o"><=</span> <span class="nx">canvasWidth</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">background1</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="o">-</span><span class="nx">canvasHeight</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">background2</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
<span class="nx">size_of_visible</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>C'est gagné ?! Oui ... mais non ... uniquement sur Firefox !! Mais sous des navigateurs utilisant WebKit, le fond ne s'affiche pas.</p>
<h2>Retour sur une façon dégueulasse de faire</h2>
<p>La façon de procéder n'est pas propre car au final, nous gèrons une image bien plus grande que le canvas ne peut supporter. Firefox étant bien foutu, il s'accomode de cet situation en n'affichant que ce qui est visible dans canvas. Mais dans Webkit, c'est comme si nous cherchons à obtenir des dépassements de tableaux. Le canvas ne doit gérer que ce qui fait parti de son cadre d'affichage. Au delà, c'est inutile.</p>
<p>Pour corriger cela, nous allons "cropper" chaque image afin de ne garder que ce qui est destiné à l'affichage. Expérimentons un peu :</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderBackgrounds</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span>
<span class="c1">// Calcul du reste à afficher</span>
<span class="kd">var</span> <span class="nx">background1_shownX</span> <span class="o">=</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span> <span class="o">+</span> <span class="nx">background1_posX</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">background1_shownX</span> <span class="o">></span> <span class="nx">canvasWidth</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">background1_shownX</span> <span class="o">=</span> <span class="nx">canvasWidth</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Affichage trame principal </span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">background1</span><span class="p">,</span>
<span class="o">-</span><span class="nx">background1_posX</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="o">-</span><span class="nx">canvasHeight</span><span class="p">,</span> <span class="nx">background1_shownX</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">background1_shownX</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Cela résoud le problème. Bien entendu, cette variable <strong>background1_shownX</strong> ne sert à rien car nous avions déjà défini la même pour le calcul de la copie décalée, nommée <strong>size_of_visible</strong></p>
<p>Nous allons donc utiliser la même technique de \"cropping\" pour la
copie décalée</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderBackgrounds</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Calcul de l'affichable</span>
<span class="p">...</span>
<span class="c1">// Affichage trame principal </span>
<span class="p">...</span>
<span class="c1">// position de la copie décalée</span>
<span class="kd">var</span> <span class="nx">size_of_visible</span> <span class="o">=</span> <span class="nx">background1_posX</span> <span class="o">+</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">size_of_visible</span> <span class="o"><</span> <span class="nx">canvasWidth</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">background1</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="o">-</span><span class="nx">canvasHeight</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="o">-</span><span class="nx">size_of_visible</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">,</span>
<span class="nx">size_of_visible</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="o">-</span><span class="nx">size_of_visible</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Ce qui peut donner après factorisation de l'ensemble :</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderBackgrounds</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Calcul de l'affichable pour le fond 1</span>
<span class="kd">var</span> <span class="nx">size_of_visible</span> <span class="o">=</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span> <span class="o">+</span> <span class="nx">background1_posX</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">size_of_visible</span> <span class="o">></span> <span class="nx">canvasWidth</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">size_of_visible</span> <span class="o">=</span> <span class="nx">canvasWidth</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Affichage trame principal </span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">background1</span><span class="p">,</span>
<span class="o">-</span><span class="nx">background1_posX</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="o">-</span><span class="nx">canvasHeight</span><span class="p">,</span> <span class="nx">size_of_visible</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">size_of_visible</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="c1">// position de la copie décalée</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">size_of_visible</span> <span class="o"><</span> <span class="nx">canvasWidth</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">background1</span><span class="p">,</span>
<span class="mf">0</span><span class="p">,</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">height</span><span class="o">-</span><span class="nx">canvasHeight</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="o">-</span><span class="nx">size_of_visible</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">,</span>
<span class="nx">size_of_visible</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="o">-</span><span class="nx">size_of_visible</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Nous relançons la démonstration <a href="http://kyoku57.org/demo/animations-canvas/demo_01/">disponible ici</a></p>
<p>Et là, c'est bon ! Cela fonctionne aussi bien sous Firefox que sous Chrome et compagnie.</p>
<h2>Améliorer l'effet avec un double fond</h2>
<p>Comme vous l'avez constaté en lançant la démonstration, nous avons un double décor. Dans de nombreux jeux, plusieurs fonds sont utilisés pour donner un effet de profondeur. Celui en arrière plan défile plus vite que ceux qui sont placés plus en avant plan de la scène afin de rendre cet effet.</p>
<p>Pour cela, rien de plus simple : il suffit d'ajouter un deuxième fond comme on peut le voir sur l'image suivante.</p>
<p><img alt="notre deuxième fond sur le premier" src="https://sebastien-dupire.info/pictures/0009-background1et2.jpg"></p>
<p>Une fois l'image définie, il ne restera qu'à :</p>
<ul>
<li>ajouter une variable <strong>background2_posX</strong> pour la position du fond avant</li>
<li>pré-charger le fond via l'objet <strong>background2</strong></li>
<li>ajouter le calcul de cette position dans <strong>computeBackgroundsPositions</strong> (le deuxième fond allant plus vite)</li>
<li>ajouter le rendu de ce fond avant dans <strong>renderBackgrounds</strong></li>
</ul>
<h2>Se servir du turbo pour accélérer le défilement</h2>
<p>Vous souvenez vous de la variable <strong>rI</strong>, qui permet d'ajouter des unités de déplacement en prenant en compte une accélération sommaire ? Nous pouvons l'utiliser pour accélérer nos fonds lors d'une pression sur la touche TURBO. Il suffit de l'ajouter dans le traitement des positions</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">computeBackgroundsPositions</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">background1_posX</span> <span class="o">-=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="mf">4</span> <span class="o">+</span> <span class="nx">rI</span><span class="p">);</span>
<span class="nx">background1_posX</span> <span class="o">=</span> <span class="nx">background1_posX</span> <span class="o">%</span> <span class="nx">background1</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
<span class="nx">background2_posX</span> <span class="o">-=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="mf">6</span> <span class="o">+</span> <span class="nx">rI</span><span class="p">);</span>
<span class="nx">background2_posX</span> <span class="o">=</span> <span class="nx">background2_posX</span> <span class="o">%</span> <span class="nx">background2</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<h2>La suite ?</h2>
<p>Nous voyons bien qu'en ajoutant des ressources et des fonds animés, nous pourrons, lors du refactoring, prévoir pas mal de petites choses :</p>
<ul>
<li>créer un manager de ressources</li>
<li>un autre pour le calcul des positions</li>
<li>un autre pour les animations</li>
</ul>
<p>Ce sera l'objet d'un prochain billet attaquant le refactoring de l'existant.</p>
<h2>Demo Live et code source</h2>
<p>La démo qui utilise le code vu durant ce tutoriel est <a href="http://kyoku57.org/demo/animations-canvas/demo_01/">disponible ici</a>.</p>
<p>Le code source de cette démo est <a href="https://bitbucket.org/kyoku57/animation-canvas-demos/src/">ici</a>. (dépôt Mercurial)</p>Un an dans le monde Java2013-01-15T00:00:00+01:002013-01-15T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2013-01-15:/un-an-dans-le-monde-java.html<p>L'an dernier, j'ai quitté l'ancienne boîte dans laquelle je travaillais sur Metz afin de rejoindre ma boîte actuelle située au Luxembourg. Avant, je faisais du PHP/MySQL, Javascript, du Python et plein d'autres choses comme de l'administration système et autres. Je regardais pas mal d'articles de Pythoniens qui aimaient bien …</p><p>L'an dernier, j'ai quitté l'ancienne boîte dans laquelle je travaillais sur Metz afin de rejoindre ma boîte actuelle située au Luxembourg. Avant, je faisais du PHP/MySQL, Javascript, du Python et plein d'autres choses comme de l'administration système et autres. Je regardais pas mal d'articles de Pythoniens qui aimaient bien troller au sujet de Java (quand ce n'était pas PHP), et cela a attisé ma curiosité. Qu'est ce qui peut faire que ce langage soit si mal aimé des utilisateurs de langages dynamiques alors que la plateforme Android qui était en plein boom, en faisait son langage de prédilection ? La haine était-elle liée aux salaires médians plus intéressants des développeurs Java par rapport aux autres langages ? Java est-il aussi merdique ? Peu importe la raison, je me disais que, quitte à changer de boulot, autant essayer Java/J2EE et son écosystème afin de se forger son propre avis. </p>
<p>J'ai eu de la chance, via Twitter, de rencontrer mon directeur actuel qui a eu le cran, voir la folie de m'embaucher alors que je n'avais quasiment jamais touché à l'univers Java en milieu professionnel. D'ailleurs, chose marrante : mon précédent boss, m'avait embauché pour faire du PHP alors que je n'en avais quasiment jamais fait avant. Cela doit être une constante dans ma vie : me faire embaucher sur un truc que je ne sais pas faire. A moins que je sois systématiquement embauché pour mes blagues pourries ... allez savoir !</p>
<p><img alt="Sans blaguer, j'ai été embauché suite à ce tweet" src="https://sebastien-dupire.info/pictures/0008-blague-twitter.jpg"></p>
<p>Passés les entretiens d'embauche avec l'équipe, début Janvier 2012, je découvrais le Luxembourg, mon nouveau poste et ma première mission. C'était assez excitant !</p>
<h2>Le langage Java</h2>
<p>J'avais fait un peu de Java en 2001 : lire un code Java afin de fournir un code similaire en C++. Mis à part le fait que Java était d'une lenteur à vomir, je dois avouer que j'étais séduit par ce langage où le mot clef <strong>delete</strong> n'allait plus hanter mes nuits. Que Dieu bénisse le garbage collector !</p>
<p>Arrivé sur ma première mission, je me suis plongé à hauteur de 50% dans Java, les autres pourcents étant réservés aux réunions chronovores et à la découverte des joies de l'administration et des contraintes d'accès. Le sentiment que j'avais sous Android s'amplifiait : mais pourquoi les Javaistes se compliquent autant la vie ? Pourquoi un fichier par classe ? Pourquoi autant d'inferfaces avec une seule implémentation pour chacune ? Pourquoi une architecture avec autant de couches ?</p>
<p>Le langage Java n'est pas à jeter : il est certes verbeux, mais reste assez lisible. A force de l'utiliser, on utilise toujours et toujours les mêmes types d'objets, les mêmes design patterns et puis, avouons le : depuis les annotations, il y a un paquet de choses qui sont devenus beaucoup plus facile à réaliser. Je pense notamment aux WebServices, à la persistance via JPA, les EJBs. J'avoue même, sans honte, que parfois j'aime coder en Java.</p>
<p><img alt="Java, ça peut faire mal" src="https://sebastien-dupire.info/pictures/0008-cactus.jpg"></p>
<h2>Les bibliothèques / frameworks</h2>
<p>Il faut avouer une chose : c'est riche ! Les bibliothèques Java couvrent vraiment tous les usages et je ne peux pas dire aujourd'hui "non, je ne peux pas le faire en Java". Tout est faisable, il suffit juste d'avoir de la patience et du courage pour trouver ce que l'on cherche.</p>
<p>Il faut avouer une autre chose : c'est le bordel ! Si on utilise pas Java tous les jours, le ticket d'entrée est rude. Même avec Maven, je trouve que les bibliothèques et dépendances sous Java sont dans un bordel sans nom et pour retrouver la bibliothèque qui va faire ce qu'on lui demande, il faut se lever tôt ou savoir se servir de Google car même le <a href="http://search.maven.org/">Maven Central repository</a> est parfois pas évident et pas très clair.</p>
<p><img alt="java et sa grande bibliothèque" src="https://sebastien-dupire.info/pictures/0008-librairie.jpg"></p>
<p>Ce qui me saoule, ce sont les différentes implémentations d'une même bibliothèque. Difficile de savoir si celle qu'on récupère est la bonne et surtout si elle fonctionne. Je pense notamment à l'implémentation IBM de la classe Base64 qui ne donne pas les mêmes résultats en 32 bits et 64 bits, alors que celle du package common de Java fait très bien le boulot.</p>
<p>D'un point de vue frameworks, j'ai juste vu comment fonctionnaient les applications Web Java classiques, ainsi que celles utilisant Struts 2. Autant dire qu'ayant goûté à Django, Pylons ou autre comme Bottle, l'utilisation de Struts 2 est une catastrophe pour moi. Que ce soit dans la manière de router les URLs, où de gérer les ressources avec une tétrachié de fichiers XML partout. A terme, on s'en accomode mais on ne s'y fait jamais. Je ne dis pas que c'est mauvais, mais je n'en suis pas fan.</p>
<h2>La documentation</h2>
<p>C'est peut-être une critique infondée, mais globalement, je trouve la ocumentation Java très mal foutue et souvent pas assez décorée d'exemples. Par moment, c'est simplement pas à jour. La plupart du temps, je ne trouve que des Javadocs d'API, ce qui, pour moi qui débutait, était tout sauf simple. A quoi sert d'avoir le détail d'une méthode, si ni le test unitaire, ni un exemple n'est fourni avec. Je n'aime pas jouer aux devinettes à ce niveau.</p>
<p><img alt="Convention de nommage par moment" src="https://sebastien-dupire.info/pictures/0008-jeanpierre.jpg"></p>
<p>Si je prends par exemple le site officiel de la bibliothèque <a href="http://www.displaytag.org/1.2/">display tag</a> , ça pique les yeux. Certaines pages sont en erreur 404 (souvent celles dont j'ai besoin) et au final, je passe mon temps sur les forums d'entraide comme <a href="http://www.stackoverflow.com/">stackoverflow</a> seulement parce que l'information que je cherche n'est pas sur le site officiel (ou du moins, pas facilement accessible).</p>
<p>Je me demande si les mainteneurs de bibliothèques Java, ne font pas exprès de pourrir la documentation online afin de nous vendre, à fort tarif, des bouquins sur l'utilisation de Java et de son écosystème. Les bouquins sur Java, il en existe beaucoup, et des très bons, mais je reste persuadé qu'une bonne documentation en ligne complétée par des exemples, vaudra tous les bouquins du monde ... surtout quand ces mêmes bouquins ne font que donner des exemples d'utilisations courantes.</p>
<h2>Les outils logiciels</h2>
<p>Alors oui, je l'avoue : j'utilise RAD, Websphere et DB2 dans le cadre de mes missions actuelles. RAD étant un dérivé d'Eclipse à la sauce IBM, il est normal d'avoir les mêmes défauts : support de SVN un peu à la limite, plugin Maven obsolète qui te pond des projets en Java 1.4 par défaut et qui a du mal à se synchroniser quand on travaille sur plusieurs workspaces en même temps. Bref, c'est lent, ça rame, ça plante et même s'il y a de bonnes choses, globalement, je suis très déçu par cet environnement. Parait-il que j'ai commencé par le pire IDE qui n'a jamais été fait pour Java.</p>
<p>Le serveur d'application que j'utilise est WebSphere 7.5. Le WAS est d'une lenteur sans nom, ça bouffe une quantité incroyable de RAM, à configurer c'est horrible, avec une interface graphique qui date et qui n'est pas toujours très claire. Les JSP ne se rechargent pas toujours, ça ne répond qu'une fois sur deux, mais on ne sait pas pourquoi, les logs ne s'affichent pas, bref ... J'espère qu'IBM a fait mieux depuis car là, c'est tout sauf vendeur. J'ai eu l'occasion de tester <a href="http://glassfish.java.net/">GlassFish</a> et c'est le jour et la nuit, niveau simplicité de mise en oeuvre.</p>
<p>Comme vous l'avez vu plus haut, j'utilise <a href="http://subversion.tigris.org/">Subversion</a>. Heureusement, c'est une version récente (avec un seul .svn à la racine du projet), mais après avoir passé quatre années sous <a href="http://mercurial.selenic.com/">Mercurial</a> et maintenant que j'utilise aussi <a href="http://git-scm.com/">Git</a>, revenir sur SVN est assez douloureux. On subit les commits foireux d'autres personnes, et les caprices du plugin qui ne veut plus synchroniser car il manque un fichier ou qu'il a été écrasé avec une update précédente. J'oublie toujours que je n'ai pas à faire de push après un commit, et cela me perturbe. De plus, étant intégré dans RAD, je suis obligé plus ou moins de l'utiliser en mode graphique et non en ligne de commande. Utiliser un autre outil à côté
comme <a href="http://tortoisesvn.tigris.org/">Tortoise</a>, et vous obtenez de belles surprises en revenant dans l'IDE.</p>
<p>Les bonnes surprises pour moi sont <a href="http://maven.apache.org/">Maven</a>, <a href="http://jenkins-ci.org/">Jenkins</a> et <a href="http://www.sonarsource.org/">Sonar</a> : l'intégration continue en utilisant ces outils est vraiment sympa. Jenkins a le mérite d'être simple et assez complet notamment grâce à tous ses plugins, et les rapports pondus par Sonar aident vraiment à déceler des soucis d'implémentations que l'on n'aurait pas forcement vus. Enfin, comment s'en sortir sans Maven ? Je peste par moment contre lui et ses pom.xml mais la dernière fois que j'ai vu un script Ant de compilation, j'ai
failli me pendre (encore de très jolis fichiers XML). Par contre, je trouve que Maven télécharge beaucoup trop de choses. Faites un projet "Offline" en Java qui utilise Maven, et vous allez avoir une surprise en regardant la taille de votre repository : on dirait qu'il a téléchargé tout Internet rien que pour votre projet.</p>
<p><img alt="Maven l'usine qui copie le Net" src="https://sebastien-dupire.info/pictures/0008-maven.jpg"></p>
<h2>Le hardware utilisé</h2>
<p>Alors, globalement, je ne suis pas mal loti. J'ai un PC très récent (i5 + 8 Go de RAM + double moniteur). Mais il faut au moins ça pour faire tourner son IDE, son serveur d'application et son serveur DB2 Express. Autre problème : je suis sous Windows et c'est ... lourd. Je ne compte plus les écrans bleus de la PIC après une arrivée tôt le matin. Le truc qui me tue est qu'avant, je me contentais d'un Pentium 4 avec 2 Go de RAM pour programmer en Python et PHP, et bien que j'avais une base de données, un serveur Web, et parfois même une tripotée de programmes parasites autour, ça ne ramait quasiment jamais. Au moins, Java a le mérite de rendre heureux les revendeurs de matériel et d'offrir une justification supplémentaire à l'emploi de centrales
nucléaires dans le coin.</p>
<p><img alt="Maven Centrale" src="https://sebastien-dupire.info/pictures/0008-centrale.jpg"></p>
<p>Luxembourg oblige, je travaille sur un clavier Qwertz et même si on s'y fait, le plus dur est de switcher entre son clavier perso et son clavier pro. Je pourrais très bien mettre mon clavier français en USB dessus, ou remapper les touches, ou amener un Bepo .. mais je préfère m'y faire, pour le jour où je serais sur une mission où il sera impossible d'ajouter son propre clavier.</p>
<p><img alt="Le Qwertz" src="https://sebastien-dupire.info/pictures/0008-aidezmoi.jpg"></p>
<h2>Les méthodes de travail</h2>
<p>Quand je suis arrivé, l'équipe était en pleine restructuration et la méthode "agile" était sur toutes les lèvres. Réunion rapide chaque matin, mise à jour du Kanban qui a évolué plus d'une fois, retrospective hebdomadaire ... C'est instructif et permet d'avoir une bonne vision d'ensemble sur un projet. Après tout n'est pas parfait, mais globalement, on arrive à s'adapter aux demandes, et je pense que c'est l'essentiel.</p>
<p>S'il y a bien une chose qui me manque, c'est la maitrise de la chaine de valeur. Dans mon ancienne boîte, une application Web se concevait du début à la fin, en incluant la mise en production. Sur ma mission actuelle, il y a une équipe pour chaque chose (WAS, Batch, DB2, droits, etc ...) : c'est bien, mais ça transforme une simple mise à jour applicative en étape du Paris/Dakar. Et durant cette étape, les autres équipes n'en ont rien à faire de vos méthodologies agiles ou de votre capacité à suivre les désirs de votre client : il faut suivre la procédure, leurs procédures... et certaines fois, la procédure, c'est juste pas réaliste.</p>
<p><img alt="Les joies de la mise en production" src="https://sebastien-dupire.info/pictures/0008-production.jpg"></p>
<p>Le point que je déteste le plus est l'emploi des formats Microsoft pour la documentation : Excel, PowerPoint, et Word. Pire ! Cette documentation n'est pas avec le projet, mais à côté, dans une arborescence parallèle. Tu veux mettre à jour ta documentation, il faut que personne ne l'ouvre sur le réseau sous risque qu'il soit locké. Tu ne peux pas versionner proprement les modifications et faire des diffs. Qu'on ne me parle pas du Track Changes sous Word, où on se retrouve avec du rouge et des ratures imbouffables partout. Pour moi, la documentation du projet, c'est au plus proche du code, et si possible dans un format simple : <a href="http://fr.wikipedia.org/wiki/ReStructuredText">reStructuredText</a> ou <a href="http://fr.wikipedia.org/wiki/Markdown">Markdown</a> avec un générateur
pour éventuellement pondre cette même documentation au format désiré.</p>
<p>En contrepartie, j'ai des collègues géniaux sur qui je peux compter. Ils ont tous de la bouteille en Java et du coup, il est facile pour moi de progresser rapidement avec autant de ressources à ma disposition.</p>
<p><img alt="L'esprit d'équipe" src="https://sebastien-dupire.info/pictures/0008-allezcrever.jpg"></p>
<h2>Et moi dans tout ça ?</h2>
<p>Je suis plutôt satisfait de mon changement de carrière. L'objectif de découvrir Java/J2EE est rempli et maintenant que j'ai passé la barrière du langage, je vais pouvoir enfin m'attaquer aux choses sérieuses et passer au niveau 2. J'apprends beaucoup, surtout à relativiser. Techniquement, Java est intéressant même si je ne lui prédis pas un grand avenir. Les clients veulent des choses beaucoup plus rapidement, plus sécurisées, mieux maintenables, plus \"au goût du jour\" et surtout moins chères. Je ne sais pas pour vous, mais si on me demande tout ça, je ne pense absolument pas à Java, sans vouloir troller. Une petite frustration néanmoins, est de mettre rendu compte que ce que j'avais réalisé lors de ma première mission aurait pu être plié en un mois à fond avec Python, là où j'en ai mis sept avec Java. Ce n'est vraiment pas le même monde et je ne peux pas dire que c'est entièrement de la faute de Java mais n'empêche que je suis persuadé que la productivité avec un langage comme Python doit être bien plus grande.</p>
<p>A l'issue de cette nouvelle année de développement, je ne me considère toujours pas comme un développeur référent et/ou bon, mais plus comme un "junior" en constant éveil. Le temps me manque pour tester tout ce que j'aimerais tester (j'ai d'autres loisirs à côté aussi). J'ai moins de temps libre que j'en avais dans mon précédent boulot, temps de trajet oblige. Du coup, ces derniers temps, j'ai beaucoup plus lu qu'expérimenté et cela me manque beaucoup.</p>
<h2>Résumé de ce premier 3615 MyLife</h2>
<p>Si je dois résumer tout ce qui a été dit plus haut après une année avec Java et une petite partie de son écosystème :</p>
<ul>
<li>le langage n'est pas à craindre : Java reste un langage complet et répondant à la plupart des problèmatiques</li>
<li>ses bibliothèques sont riches mais désorganisées, avec une documentation mal fichue \"à mon goût\" (je précise)</li>
<li>ses frameworks sont peut-être bien, je n'ai pas assez de recul. Mais personnellement, je ne suis pas fan de Struts 2 déjà</li>
<li>si vous devez avoir un IDE, oubliez et fuyez Eclipse et RAD</li>
<li>pas de nouveau projet Java sans Maven, sinon, envisagez sérieusement le suicide</li>
<li>Jenkins et Sonar sont vos amis et je suis sûr qu'il existe d'autres amis essentiels au Javaiste</li>
<li>par pitié : laissez crever SVN et CVS et orientez vous d'office sur Mercurial et/ou Git</li>
<li>la méthode de travail est la même pour tous les langages : communiquez entre vous et avec le client, faites le souvent, restez réaliste et tout ira bien</li>
<li>mettez votre doc dans un format universel et au plus proche du code. Réservez la documentation MS Office pour faire de la documentation destinée au fly-fucking</li>
<li>vos collègues sont une ressource précieuse, il faut en abuser et ne pas hésiter à donner de votre personne en retour. Ce fût le cas dans mon ancienne boîte, et j'ai de la chance que ce soit encore le cas dans ma boîte actuelle</li>
</ul>
<p>L'année 2013 va être très sympa. Pourvu que ça dure !</p>
<p>Technologies et outils découverts et/ou utilisés durant cette année :</p>
<ul>
<li>Java / J2EE</li>
<li>Struts 2</li>
<li>OpenJPA / Hibernate</li>
<li>IBM DB2, RAD et WebSphere</li>
<li>Maven / Jenkins / Sonar</li>
<li>JWay Formpublisher</li>
<li>Scala et Play!2 (trop peu encore)</li>
</ul>Performance de la POO avec Javascript2011-12-19T00:00:00+01:002011-12-19T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-12-19:/comparer-les-performances-de-structures-objets-en-javascript.html<p>Vous vous attendiez à la suite sur la création de notre jeu d'avion en Javascript + Canvas ? Au risque de vous décevoir, il reste un point que nous avons pas vu lors du <a href="https://sebastien-dupire.info/faire-de-la-poo-avec-javascript.html">dernier billet</a> : la performance liée à l'utilisation de la POO. En effet, j'ai annoncé que d'instancier une classe …</p><p>Vous vous attendiez à la suite sur la création de notre jeu d'avion en Javascript + Canvas ? Au risque de vous décevoir, il reste un point que nous avons pas vu lors du <a href="https://sebastien-dupire.info/faire-de-la-poo-avec-javascript.html">dernier billet</a> : la performance liée à l'utilisation de la POO. En effet, j'ai annoncé que d'instancier une classe déclarée à la <em>dégueulasse</em> était plus lent et gourmand en mémoire que d'instancier une classe déclarée avec l'objet <em>prototype</em>. Plutôt que de me croire sur parole pourquoi ne pas tester tout ça et transformer ces belles paroles en preuves irréfutables ? C'est ce que je vous propose de suivre durant ce billet.</p>
<h2>T'es Pile dans le Tas</h2>
<p>Chaque programme qui s'éxecute s'appuie sur trois types d'allocation de la mémoire vive (RAM).</p>
<ul>
<li>l'allocation statique</li>
<li>l'allocation dynamique sur la pile</li>
<li>l'allocation dynamique sur le tas</li>
</ul>
<p><strong>l'allocation statique</strong> (text) est faite par des constantes au sein du programme. Dans un langage compilé, l'espace à allouer est défini à la compilation en binaire, et dès le début de l'éxecution du programme, l'espace dédié est alloué et est à taille fixe pour toute la durée du programme. En Javascript, nous ne pouvons pas faire d'allocation statique car le langage étant interprété, il n'est pas possible de définir une quantité de mémoire à allouer avant l'éxecution du programme.</p>
<p><strong>l'allocation dynamique sur la pile</strong> (stack) est faite au moment où l'on rentre dans une fonction. Le contexte est mémorisé, et toute variable (paramètres de la fonction) est ajouté à la pile. C'est une simple LIFO (Last In First Out), et à la sortie de la fonction, la pile est désallouée du dernier élement entré au premier. Cette allocation se fait de manière automatique et ce n'est pas le programmeur qui décide de cette allocation mais la structure de son programme.</p>
<p><strong>l'allocation dynamique sur le tas</strong> (heap) est faite dès qu'un besoin en mémoire durant le programme est nécessaire et qu'il est réalisé par le programme. C'est le cas lorsqu'on déclare une nouvelle variable, ou une nouvelle instance.</p>
<p>Pour la suite du billet, la seule chose qui va nous intéresser ici sera l'allocation dynamique sur le tas, seule partie que nous pouvons maitriser/optimiser dans notre script Javascript.</p>
<h2>Le script de test</h2>
<p>Il contient le nécessaire pour réaliser des mesures de temps d'éxecution.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><!DOCTYPE html></span>
<span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"fr"</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"Content-Type"</span> <span class="na">content</span><span class="o">=</span><span class="s">"text/html; charset=utf-8"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>Performance de la POO avec Javascript<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/css"</span> <span class="na">href</span><span class="o">=</span><span class="s">"../common/css/demo.css"</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/javascript"</span><span class="p">></span>
<span class="c1">// Insérer votre code ici</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">><</span><span class="nt">h1</span><span class="p">></span>Performance de la POO avec Javascript<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">><</span><span class="nt">strong</span><span class="p">></span>NOTE<span class="p"></</span><span class="nt">strong</span><span class="p">></span> : ce test ne fonctionnera que sur Firefox et Chrome (toute version)<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span><span class="cm"><!-- Bouton d'action --></span>
Créer
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">id</span><span class="o">=</span><span class="s">"iteration"</span> <span class="na">value</span><span class="o">=</span><span class="s">"2000000"</span> <span class="p">/></span> instances de
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"button"</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"testObjetsTypeA();"</span> <span class="na">value</span><span class="o">=</span><span class="s">"ClassA (constructeur)"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"button"</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"testObjetsTypeB();"</span> <span class="na">value</span><span class="o">=</span><span class="s">"ClassB (prototype)"</span> <span class="p">/></span> ou
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"button"</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"resetTest();"</span> <span class="na">value</span><span class="o">=</span><span class="s">"Effacer les références aux objets"</span> <span class="p">/></span>
<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span><span class="cm"><!-- Champs de debug --></span>
Total Time :
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">id</span><span class="o">=</span><span class="s">"debug_gap"</span> <span class="na">value</span><span class="o">=</span><span class="s">"0"</span> <span class="p">/></span> ms<span class="p"><</span><span class="nt">br</span><span class="p">/></span>
Pour vérifier la consommation mémoire, tapez <span class="p"><</span><span class="nt">strong</span><span class="p">></span>about:memory<span class="p"></</span><span class="nt">strong</span><span class="p">></span> dans la barre d'adresse de votre navigateur.
<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>Et nous mettrons en place le Javascript. Tout d'abord, mettons en place les définitions de nos deux classes à tester : <em>ClasseA</em> et <em>ClasseB</em>.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Version avec constructeur</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">multiplie</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">*</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">divide</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">/</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">test</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o"><</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * Version avec Prototype</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">ClasseB</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">multiplie</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">*</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">divide</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">/</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">test</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o"><</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">a</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Ces deux classes font strictement la même chose. Seules les façons de les définir diffèrent.</p>
<p>Nous allons maintenant ajouter le nécessaire pour les tester.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/** </span>
<span class="cm"> * Méthode pour déterminer le temps passé entre deux appels </span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">exec_time</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="cm">/* Temps d'execution */</span>
<span class="kd">var</span> <span class="nx">timeref</span> <span class="o">=</span> <span class="p">(</span><span class="ow">new</span> <span class="nb">Date</span><span class="p">()).</span><span class="nx">getTime</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">debug_gap</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="kd">var</span> <span class="nx">new_time</span> <span class="o">=</span> <span class="p">(</span><span class="ow">new</span> <span class="nb">Date</span><span class="p">()).</span><span class="nx">getTime</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">diff</span> <span class="o">=</span> <span class="nx">new_time</span> <span class="o">-</span> <span class="nx">timeref</span><span class="p">;</span>
<span class="nx">timeref</span> <span class="o">=</span> <span class="nx">new_time</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">diff</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * Création de 'nombreIter' objets de type ClasseA</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">testObjetsTypeA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">nombreIter</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'iteration'</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* démarrage du compteur */</span>
<span class="nx">listObject</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mf">0</span><span class="p">;</span> <span class="nx">i</span><span class="o"><</span><span class="nx">nombreIter</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">myclass</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">(</span><span class="mf">3</span><span class="p">,</span><span class="mf">2</span><span class="p">);</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">diff</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">multiplie</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">divide</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">test</span><span class="p">();</span>
<span class="nx">listObject</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">myclass</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">exec_time</span> <span class="o">=</span> <span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* arrêt du compteur */</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'iteration'</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">nombreIter</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'debug_gap'</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">exec_time</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * Création de 'nombreIter' objets de type ClasseB</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">testObjetsTypeB</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">nombreIter</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'iteration'</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* démarrage du compteur */</span>
<span class="nx">listObject</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mf">0</span><span class="p">;</span> <span class="nx">i</span><span class="o"><</span><span class="nx">nombreIter</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">myclass</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseB</span><span class="p">(</span><span class="mf">3</span><span class="p">,</span><span class="mf">2</span><span class="p">);</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">diff</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">multiplie</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">divide</span><span class="p">();</span>
<span class="nx">myclass</span><span class="p">.</span><span class="nx">test</span><span class="p">();</span>
<span class="nx">listObject</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">myclass</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">exec_time</span> <span class="o">=</span> <span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* arrêt du compteur */</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'iteration'</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">nombreIter</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'debug_gap'</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">exec_time</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * On efface les objets en mémoire</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">resetTest</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">listObject</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'debug_gap'</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<h2>Protocole de test</h2>
<p>Notre test ne s'effectuera que sous Mozilla Firefox et Google Chrome qui disposent d'outils pour mesurer la mémoire allouée par notre script de test.</p>
<p>Ce test est <a href="http://kyoku57.org/demo/animations-canvas/test_00/">disponible
ici</a>.</p>
<p>1) Lancer un Firefox ou un Chrome tout frais
2) Aller à l'URL de notre page de
<a href="http://kyoku57.org/demo/animations-canvas/test_00/">test</a>.
3) Définir le nombre d'objet à créer
4) Cliquer sur le type de classe à tester
5) Noter le temps d'execution et vérifier la consommation mémoire :
sous
<a href="http://www.geeek.org/post/2008/09/02/Google-Chrome-Tips-%3A-about%3Amemory">Chrome</a>
ou sous
<a href="http://www.linuxinsight.com/firefox-4-about-memory.html">Firefox</a></p>
<p>Dans tous nos tests nous partirons sur 2000000 objets à créer.</p>
<h2>Machine et navigateurs de test</h2>
<p>La machine de test est un PC sous Ubuntu 11.10 64 bits sous Gnome Shell avec accélération graphique opérationnelle (AMD) et processeur Core 2 Duo E8400 de 3 GHz et 4 Go de RAM DDR2.</p>
<p>La version de Mozilla Firefox est la 8.0 (paquet officiel).</p>
<p>La version de Google Chrome est la 16.0.912.63 (paquet deb64 du site source).</p>
<h2>Tests avec la déclaration par constructeur</h2>
<p>Nous allons instancier 2000000 objets de type <em>ClasseA</em></p>
<h3>Résultats sous Mozilla Firefox</h3>
<p><strong>Temps d'éxecution</strong> : 827 ms</p>
<p><strong>Empreinte mémoire</strong> :</p>
<div class="highlight"><pre><span></span><code><span class="m">355</span>,701,235 B <span class="o">(</span><span class="m">53</span>.19%<span class="o">)</span> -- compartment<span class="o">(</span>http://kyoku57.org/demo/animations-canvas/test_00/<span class="o">)</span>
│ │ ├──210,268,160 B <span class="o">(</span><span class="m">31</span>.44%<span class="o">)</span> -- gc-heap
│ │ │ ├──208,080,712 B <span class="o">(</span><span class="m">31</span>.12%<span class="o">)</span> -- objects
│ │ │ ├────1,642,720 B <span class="o">(</span><span class="m">00</span>.25%<span class="o">)</span> -- arena-headers
│ │ │ ├──────413,216 B <span class="o">(</span><span class="m">00</span>.06%<span class="o">)</span> -- arena-padding
│ │ │ ├───────87,872 B <span class="o">(</span><span class="m">00</span>.01%<span class="o">)</span> -- shapes
│ │ │ └───────43,640 B <span class="o">(</span><span class="m">00</span>.01%<span class="o">)</span> -- arena-unused
│ │ ├──144,789,088 B <span class="o">(</span><span class="m">21</span>.65%<span class="o">)</span> -- object-slots
│ │ ├──────404,552 B <span class="o">(</span><span class="m">00</span>.06%<span class="o">)</span> -- tjit-data
│ │ │ ├──172,224 B <span class="o">(</span><span class="m">00</span>.03%<span class="o">)</span> -- trace-monitor
│ │ │ ├──148,000 B <span class="o">(</span><span class="m">00</span>.02%<span class="o">)</span> -- allocators-reserve
│ │ │ └───84,328 B <span class="o">(</span><span class="m">00</span>.01%<span class="o">)</span> -- allocators-main
│ │ ├──────131,072 B <span class="o">(</span><span class="m">00</span>.02%<span class="o">)</span> -- tjit-code
│ │ ├───────65,536 B <span class="o">(</span><span class="m">00</span>.01%<span class="o">)</span> -- mjit-code
│ │ ├───────24,176 B <span class="o">(</span><span class="m">00</span>.00%<span class="o">)</span> -- property-tables
│ │ ├───────12,772 B <span class="o">(</span><span class="m">00</span>.00%<span class="o">)</span> -- mjit-data
│ │ └────────5,879 B <span class="o">(</span><span class="m">00</span>.00%<span class="o">)</span> -- scripts
</code></pre></div>
<h3>Résultats sous Google Chrome</h3>
<p><strong>Temps d'éxecution</strong> : 5144 ms</p>
<p><strong>Empreinte mémoire</strong> :</p>
<div class="highlight"><pre><span></span><code>Tab
Performance de la POO avec Javascript
<span class="m">928</span>,648k
</code></pre></div>
<h2>Tests avec la déclaration par prototype</h2>
<p>Nous allons instancier 2000000 objets de type <em>ClasseB</em></p>
<h3>Résultats sous Mozilla Firefox</h3>
<p><strong>Temps d'éxecution</strong> : 280 ms</p>
<p><strong>Empreinte mémoire</strong> :</p>
<div class="highlight"><pre><span></span><code>│ ├──227,700,015 B <span class="o">(</span><span class="m">40</span>.12%<span class="o">)</span> -- compartment<span class="o">(</span>http://kyoku57.org/demo/animations-canvas/test_00/<span class="o">)</span>
│ │ ├──210,272,256 B <span class="o">(</span><span class="m">37</span>.05%<span class="o">)</span> -- gc-heap
│ │ │ ├──208,080,712 B <span class="o">(</span><span class="m">36</span>.66%<span class="o">)</span> -- objects
│ │ │ ├────1,642,752 B <span class="o">(</span><span class="m">00</span>.29%<span class="o">)</span> -- arena-headers
│ │ │ ├──────413,224 B <span class="o">(</span><span class="m">00</span>.07%<span class="o">)</span> -- arena-padding
│ │ │ ├───────87,552 B <span class="o">(</span><span class="m">00</span>.02%<span class="o">)</span> -- shapes
│ │ │ └───────48,016 B <span class="o">(</span><span class="m">00</span>.01%<span class="o">)</span> -- arena-unused
│ │ ├───16,789,088 B <span class="o">(</span><span class="m">02</span>.96%<span class="o">)</span> -- object-slots
│ │ ├──────412,616 B <span class="o">(</span><span class="m">00</span>.07%<span class="o">)</span> -- tjit-data
│ │ │ ├──172,224 B <span class="o">(</span><span class="m">00</span>.03%<span class="o">)</span> -- trace-monitor
│ │ │ ├──148,000 B <span class="o">(</span><span class="m">00</span>.03%<span class="o">)</span> -- allocators-reserve
│ │ │ └───92,392 B <span class="o">(</span><span class="m">00</span>.02%<span class="o">)</span> -- allocators-main
│ │ ├──────131,072 B <span class="o">(</span><span class="m">00</span>.02%<span class="o">)</span> -- tjit-code
│ │ ├───────65,536 B <span class="o">(</span><span class="m">00</span>.01%<span class="o">)</span> -- mjit-code
│ │ ├───────23,568 B <span class="o">(</span><span class="m">00</span>.00%<span class="o">)</span> -- property-tables
│ │ └────────5,879 B <span class="o">(</span><span class="m">00</span>.00%<span class="o">)</span> -- scripts
</code></pre></div>
<h3>Résultats sous Google Chrome</h3>
<p><strong>Temps d'éxecution</strong> : 668 ms</p>
<p><strong>Empreinte mémoire</strong> :</p>
<div class="highlight"><pre><span></span><code>Tab
Performance de la POO avec Javascript
<span class="m">126</span>,452k
</code></pre></div>
<h2>Interprétation des résultats</h2>
<p>Nous n'allons pas comparer les navigateurs. Ce n'est pas l'objectif et ça n'aurait pas de sens pour ce test vu qu'on cherche à mesurer les gains entre deux façons de faire dans un même navigateur.</p>
<h3>Vitesse d'éxecution</h3>
<p>Première chose évidente, l'instanciation de nos objets avec notre classe définie à l'aide d'un prototype est plus rapide à l'éxecution que la même opération via notre classe définie par son unique
constructeur.</p>
<p>Pour Firefox, nous passons de 827ms à 280ms, soit <strong>un gain en vitesse
de 68%</strong>.</p>
<p>Pour Chrome, nous passons de 5144ms à 668ms soit <strong>un gain en vitesse de
87%</strong>.</p>
<h3>Occupation mémoire</h3>
<p>Là aussi, pas de surprises réelles, l'instanciation de nos objets avec notre classe définie à l'aide d'un prototype est moins gourmande en mémoire que la même opération via notre classe définie par son unique constructeur.</p>
<p>Pour Firefox, nous passons de 355701235o à 227700015o soit <strong>un gain en mémoire de 40%</strong>.</p>
<p>Pour Chrome, nous passons de 928648ko à 126452ko soit <strong>un gain en mémoire de 86%</strong>.</p>
<h2>Conclusion</h2>
<p>Ben oui, finalement je ne racontais pas de conneries lors du <a href="https://sebastien-dupire.info/faire-de-la-poo-avec-javascript.html">précédent billet</a> : il faut utiliser prototype pour déclarer nos classes. Les gains en temps de calculs et en place mémoire sont énormes et nous aurions tort de nous priver d'une optimisation aussi \"facile\". Et encore quand je dis \"optimiser\", je sous entends \"ne pas programmer comme un goret sans prendre en compte les spécificités du langage\". Il est facile de critiquer Javascript, mais encore faut il le comprendre et malgré toutes les critiques que ce langage peut essuyer, force est d'admettre qu'il revient en force depuis quelques temps et permet de réaliser de merveilleuses choses.</p>
<p>La grande question maintenant est de savoir comment font les frameworks/librairies Javascript courants qui permettent de définir nos classes plus simplement. Comment est converti un \"patron\" de classe fait avec <a href="http://www.sencha.com/products/extjs/">ExtJS</a>, <a href="http://dojotoolkit.org/">Dojo ToolKit</a> ou encore <a href="http://classy.pocoo.org/">Classy</a>?</p>
<p>Je connais la réponse : elles utilisent le pattern prototype (il suffit de regarder le code source de <a href="http://classy.pocoo.org/">Classy</a> pour s'en convaincre). Mais nous en reparlons plus tard car j'ai assez dévié de l'objectif de départ. Maintenant pour le prochain article nous reviendrons à la manipulation de notre petit avion dans un canvas.</p>
<h2>Références</h2>
<ul>
<li><a href="http://fr.wikipedia.org/wiki/Allocation_de_m%C3%A9moire">http://fr.wikipedia.org/wiki/Allocation_de_m%C3%A9moire</a></li>
</ul>POO, prototype et Javascript2011-12-18T00:00:00+01:002011-12-18T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-12-18:/faire-de-la-poo-avec-javascript.html<p>Dans le <a href="https://sebastien-dupire.info/javascript-jouer-avec-canvas-episode-1.html">billet précédent</a>,
on a défini des fonctions afin de réaliser l'animation d'un petit
avion dans un canvas. Cette introduction à l'utilisation de Javascript
permet de voir comment faire, mais si nous voulons avancer plus loin, il
va falloir trouver une autre façon de faire que d'empiler des tonnes …</p><p>Dans le <a href="https://sebastien-dupire.info/javascript-jouer-avec-canvas-episode-1.html">billet précédent</a>,
on a défini des fonctions afin de réaliser l'animation d'un petit
avion dans un canvas. Cette introduction à l'utilisation de Javascript
permet de voir comment faire, mais si nous voulons avancer plus loin, il
va falloir trouver une autre façon de faire que d'empiler des tonnes de
fonctions qui intéragissent entre elles dans un bordel sans nom. Le
mieux est de faire de la programmation orientée objet (= POO) afin de
définir chaque entité comme un objet facilement manipulable et
reproductible.</p>
<h2>Programmation orienté objet</h2>
<p>Je ne vais pas m'étendre sur le concept et rentrer dans les détails car
il existe moultes sites qui traitent du sujet.</p>
<p>Nous pouvons résumer la POO à trois principes forts :</p>
<ul>
<li><strong>encapsulation</strong> : nous pouvons mettre ensemble des propriétés
(attributs et méthodes) au sein d'un même type d'objet (une
classe). Il suffira d'instancier ce type pour obtenir un objet qui
contiendra les propriétés le définissant</li>
<li><strong>héritage</strong> : si nous avons un type A, nous pouvons déclarer un
type B qui hérite des propriétés de A et qui ajoute ses propres
propriétés</li>
<li><strong>polymorphisme</strong> : c'est le fait de pouvoir définir une même
méthode pour différents types d'objet.</li>
</ul>
<p>Avant de continuer dans ce billet, je vous invite à vous familiariser
avec le vocabulaire lié à la POO et en comprendre les différentes
notions (constructeurs, assesseurs, mutateurs, interface, etc ...)</p>
<h2>Et Javascript dans tout ça ?</h2>
<p>Javascript est tout à fait capable de faire les trois principes
ci-dessus. Javascript étant un langage de script, il a ses propres
particularités qui fait qu'on ne fait pas tout à fait la même chose en
Javascript de ce qu'on aurait l'habitude de faire en C++ ou en Java.
Pour résumer, il va falloir faire avec ses défauts et ses qualités.</p>
<p>Javascript est un langage à typage dynamique : cela signifie que
lorsqu'on déclare une variable, son type est défini à son
initialisation.</p>
<p>Les différents types peuvent être primitifs comme :</p>
<ul>
<li>les numériques (int, float, double, char),</li>
<li>les chaines de caractères (string entre ' ou \")</li>
<li>les booleans (true, false)</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">mavariable</span> <span class="o">=</span> <span class="mf">5</span><span class="p">;</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="nx">mavariable</span><span class="p">);</span>
<span class="s2">"number"</span> <span class="cm">/* mavariable est de type numérique */</span>
<span class="o">>>></span> <span class="kd">var</span> <span class="nx">monstring</span> <span class="o">=</span> <span class="s2">"foo"</span><span class="p">;</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="nx">monstring</span><span class="p">);</span>
<span class="s2">"string"</span> <span class="cm">/* monstring est de type string */</span>
<span class="o">>>></span> <span class="kd">var</span> <span class="nx">monboolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="nx">monboolean</span><span class="p">);</span>
<span class="s2">"boolean"</span> <span class="cm">/* ma variable est un type boolean */</span>
</code></pre></div>
<p>Les numériques sont par défaut des décimaux. Javascript peut aussi
définir des entiers que nous représentons soit sous forme octale (base 8
commençant par O...) soit sous forme hexadecimale (base 16 commençant
par Ox...).</p>
<blockquote>
<p>Il faut bien faire gaffe que si nous écrivons 0344 nous obtenons en
réalité l'entier 228 car 0344 est en base 8 (forme octale) à cause du 0
qui est devant.</p>
</blockquote>
<p>Nous avons deux autres types particuliers :</p>
<ul>
<li><em>null</em> qui est une constante du langage qui bien différent des autres types</li>
<li><em>undefined</em> qui est le type d'une variable qui a été ni déclarée, ni affectée</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
<span class="s2">"object"</span> <span class="cm">/* a est un type null */</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="nx">unevariablenondefinie</span><span class="p">);</span>
<span class="s2">"undefined"</span> <span class="cm">/* unevariablenondefinie est ... non définie (logique non) */</span>
</code></pre></div>
<blockquote>
<p>Une variable <em>undefined</em> n'est pas <em>null</em> et <em>null</em> n'est pas 0.</p>
</blockquote>
<p>Javascript est un langage à typage faible : cela veut dire que nous
pouvons facilement mélanger des chaines de caractères avec d'autres
types. Ce qui peut sembler être une force peut vite poser problème.</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="s2">"mon string"</span> <span class="o">+</span> <span class="mf">2</span><span class="p">;</span>
<span class="s2">"mon string2"</span>
<span class="o">>>></span> <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="s2">"mon string"</span> <span class="o">*</span> <span class="mf">2</span><span class="p">;</span>
<span class="kc">NaN</span> <span class="cm">/* Not a Number */</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="s2">"monstring"</span><span class="o">*</span><span class="mf">2</span><span class="p">);</span>
<span class="s2">"Number"</span> <span class="cm">/* ?????? Très bizarre mais ça n'a pas l'air de lui poser un soucis moral */</span>
</code></pre></div>
<p>Enfin, nous avons aussi deux autres types identifiables : les fonctions
et les objets</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">madate</span> <span class="o">=</span> <span class="ow">new</span> <span class="nb">Date</span><span class="p">();</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="nx">madate</span><span class="p">)</span>
<span class="s2">"object"</span> <span class="cm">/* ma variable est un objet */</span>
<span class="o">>>></span> <span class="nx">mafonction</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{}</span>
<span class="o">>>></span> <span class="ow">typeof</span><span class="p">(</span><span class="nx">mafonction</span><span class="p">);</span>
<span class="s2">"function"</span> <span class="cm">/* ma variable est une fonction */</span>
</code></pre></div>
<p>Dernier point fort et qui a tout son importance, c'est qu'un objet
javascript peut être modifié à tout moment. Nous pouvons librement
modifier ses attributs et ses méthodes après son instanciation (comme en
Python par exemple)</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">madate</span> <span class="o">=</span> <span class="ow">new</span> <span class="nb">Date</span><span class="p">();</span>
<span class="o">>>></span> <span class="nx">madate</span><span class="p">.</span><span class="nx">functionperso</span> <span class="o">=</span> <span class="nx">fonction</span><span class="p">(){</span> <span class="k">return</span> <span class="s2">"foo"</span><span class="p">;</span> <span class="p">}</span>
<span class="o">>>></span> <span class="nx">madate</span><span class="p">.</span><span class="nx">functionperso</span><span class="p">();</span>
<span class="s2">"foo"</span>
</code></pre></div>
<p>Dans cet exemple, nous avons défini une nouvelle méthode <em>functionperso</em>
pour notre objet <em>madate</em> de type <em>Date</em>.</p>
<h2>Les classes en Javascript</h2>
<p>En Javascript toute fonction peut être définie comme le constructeur
d'une classe portant le même nom. C'est le constructeur qui permettra
de définir ses propriétés (attributs et méthodes) grâce au mot-clef
<em>this</em>. Pour faire simple, notre fonction ainsi détaillée sera notre
classe. Il suffira d'utiliser <em>new</em> pour obtenir une instance de cette
classe (qui aura l'allure d'une fonction).</p>
<p>Nous allons définir le constructeur d'une classe nommée <em>ClasseA</em> de la
manière suivante</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Pour créer une nouvelle instance, nous allons faire</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">instance</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">(</span><span class="mf">2</span><span class="p">,</span><span class="mf">3</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">instance</span><span class="p">;</span>
<span class="nx">ClasseA</span> <span class="p">{</span> <span class="nx">attrA</span><span class="o">=</span><span class="mf">2</span><span class="p">,</span> <span class="nx">attrB</span><span class="o">=</span><span class="mf">3</span><span class="p">}</span>
<span class="o">>>></span> <span class="nx">instance</span><span class="p">.</span><span class="nx">attrA</span><span class="p">;</span>
<span class="mf">2</span>
<span class="o">>>></span> <span class="nx">instance</span><span class="p">.</span><span class="nx">attrB</span><span class="p">;</span>
<span class="mf">3</span>
</code></pre></div>
<p>Première remarque, nos attributs sont publics et sont donc accessibles
aussi bien en lecture qu'en écriture depuis l'extérieur de notre
classe.</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">instance</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">(</span><span class="mf">2</span><span class="p">,</span><span class="mf">3</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">instance</span><span class="p">.</span><span class="nx">attrA</span><span class="p">;</span>
<span class="mf">2</span>
<span class="o">>>></span> <span class="nx">instance</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="mf">4</span><span class="p">;</span> <span class="cm">/* attrA étant public, attrA sera bien modifié */</span>
<span class="o">>>></span> <span class="nx">instance</span><span class="p">.</span><span class="nx">attrA</span><span class="p">;</span>
<span class="mf">4</span>
</code></pre></div>
<p>Toute variable associé à <em>this</em> dans le constructeur sera un attribut
public. Aussi si vous désirez que cet attribut devienne privé, il faudra
simplement remplacer <em>this</em> par <em>var</em>.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/* Si on remplace ... */</span>
<span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span> <span class="cm">/* attrA est public car accessible de l'extérieur */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span> <span class="cm">/* attrB est public car accessible de l'extérieur */</span>
<span class="p">}</span>
<span class="cm">/* ... par */</span>
<span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span> <span class="cm">/* attrA devient accessible qu'à l'ensemble des propriétés du constructeur ClasseA */</span>
<span class="kd">var</span> <span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span> <span class="cm">/* attrB devient accessible qu'à l'ensemble des propriétés du constructeur ClasseB */</span>
<span class="p">}</span>
</code></pre></div>
<p>La portée d'une variable Javascript déclarée avec le mot clef <em>var</em> est
limitée à la fonction/objet qui l'encadre. Du coup, tout ce qui se
trouve dans cette fonction/objet aura accès directement à cette
variable. Par contre, de l'extérieur, elle n'existera pas. Je ne vais
pas entrer dans le détail des portées en Javascript. Cette notion, à
elle-seule mériterait un article complet, tellement c'est simple et
vicieux à la fois.</p>
<p><strong>Cas particulier</strong> : une variable qui n'a jamais été déclarée avec le
mot clef <em>var</em> appartient d'office à l'instance <em>window</em> de notre page
Web et vu que toutes les fonctions déclarées dans une page Web sont dans
l'objet <em>window</em>, elles ont accès à cette variable.</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="nx">mavar</span> <span class="o">=</span> <span class="s2">"foo"</span><span class="p">;</span>
<span class="o">>>></span> <span class="nb">window</span><span class="p">.</span><span class="nx">mavar</span><span class="p">;</span>
<span class="s2">"foo"</span>
</code></pre></div>
<p>Maintenant si vous avez bien suivi les exemples du début, une fonction
(ou méthode) n'est identifiée que par une variable. Ajouter des
méthodes à notre classe sera aussi simple que d'ajouter une propriété
dont la valeur est une fonction.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span> <span class="cm">/* premier nombre */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span> <span class="cm">/* deuxième nombre */</span>
<span class="cm">/**</span>
<span class="cm"> * Méthode a + b</span>
<span class="cm"> * @return nombre</span>
<span class="cm"> */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * Méthode a - b</span>
<span class="cm"> * @return nombre</span>
<span class="cm"> */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Si nous voulions que l'attribut <em>attrA</em> et <em>attrB</em> soient privés au
lieu d'être publics, nous aurions écrit la classe de cette façon.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span> <span class="cm">/* premier nombre */</span>
<span class="kd">var</span> <span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span> <span class="cm">/* deuxième nombre */</span>
<span class="cm">/**</span>
<span class="cm"> * Méthode a + b</span>
<span class="cm"> * @return nombre</span>
<span class="cm"> */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span> <span class="nx">attrA</span> <span class="o">+</span> <span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * Méthode a - b</span>
<span class="cm"> * @return nombre</span>
<span class="cm"> */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span> <span class="nx">attrA</span> <span class="o">-</span> <span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Pourquoi ne plus utiliser <em>this</em> dans les méthodes ? Car les attributs
<em>attrA</em> et <em>attrB</em> n'ont été déclarés et initialisés que pour la
fonction <em>ClasseA</em>. Toute méthode qui sera déclarée dans le constructeur
aura accès à ces variables.</p>
<p>Si on instancie un nouvelle objet, nous aurons aucun doute sur le
résultat</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">moninstance</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">(</span><span class="mf">3</span><span class="p">,</span><span class="mf">2</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">moninstance</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="mf">5</span>
<span class="o">>>></span> <span class="nx">moninstance</span><span class="p">.</span><span class="nx">diff</span><span class="p">();</span>
<span class="mf">1</span>
</code></pre></div>
<p>Ce code fonctionne bien entendu que les attributs soient déclarés privés
ou publics. A savoir aussi qu'en Javascript la notion de variable
protégée n'existe pas. Du coup, si nous désirons faire de l'héritage,
seules les variables publiques peuvent être utilisées. Pour le reste des
découvertes, nous resterons sur la version de classe qui possèdent les
attributs publiques.</p>
<h2>Notion d'héritage</h2>
<p>Reprenons le cas de la <em>ClasseA</em> avec des attributs publics</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Vu que les attributs et les méthodes sont de simples propriétés d'un
objet, il est facile de pouvoir faire de l'héritage. Il suffit
d'appeler la classe mère au sein du constructeur de la classe fille
pour que cette dernière reprenne l'ensemble des propriétés de la classe
mère. Nous faisons ceci grâce à la méthode <em>call</em>.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">ClasseB</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">ClasseA</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">);</span> <span class="cm">/* Appel du constructeur de la ClasseA */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* Nous redéfinissons diff */</span>
<span class="k">return</span> <span class="mf">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">multiplie</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* Nous ajoutons une méthode */</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">*</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Et à l'usage</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="nx">b</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseB</span><span class="p">(</span><span class="mf">3</span><span class="p">,</span><span class="mf">5</span><span class="p">)</span>
<span class="nb">Object</span> <span class="p">{</span> <span class="nx">attrA</span><span class="o">=</span><span class="mf">3</span><span class="p">,</span> <span class="nx">attrB</span><span class="o">=</span><span class="mf">5</span><span class="p">,</span> <span class="nx">somme</span><span class="o">=</span><span class="kd">function</span><span class="p">(),</span> <span class="nx">more</span><span class="p">...}</span>
<span class="o">>>></span> <span class="nx">b</span><span class="p">.</span><span class="nx">multiplie</span><span class="p">()</span>
<span class="mf">15</span>
<span class="o">>>></span> <span class="nx">b</span><span class="p">.</span><span class="nx">diff</span><span class="p">();</span>
<span class="mf">0</span>
<span class="o">>>></span> <span class="nx">b</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="mf">8</span>
</code></pre></div>
<p>On voit bien qu'on est dans un semblant d'héritage puisque les
attributs de A sont présents, la méthode <em>somme</em> est toujours disponible
mais <em>diff</em> est changée et nous avons ajouté une méthode <em>multiplie</em>.</p>
<p>En réalité, nous ne faisons pas vraiment de l'héritage mais de la copie
des propriétés de la classe mère et là, on met le doigt sur le problème
de cette façon de faire de la POO depuis le début de ce billet.</p>
<p>Lorsque dans un langage permettant la POO comme le C++, nous définissons
une méthode, le bout de code correspondant à la méthode de la classe ne
sera disponible qu'une fois en mémoire. Si j'ai une méthode <em>somme</em>
définie dans une <em>ClasseA</em> et qu'on instancie deux fois cette
<em>ClasseA</em>, nos deux instances utiliseront bien la même méthode en
mémoire (avec des contextes d'execution différents bien entendu). En
Javascript, cette méthode est considérée comme une simple propriété du
coup, elle est redéfinie à chaque instance. De même pour l'héritage :
si j'instancie une <em>ClasseB</em>, vu qu'il y a un <em>call</em> sur la <em>ClasseA</em>,
l'ensemble des propriétés de la <em>ClasseA</em> seront redéfinies pour la
<em>ClasseB</em> (ou recopiées selon votre façon de voir).</p>
<p>Si cette méthode est lourde et qu'on instancie mille fois notre classe
ou une de ses filles, on va avoir mille versions de la même méthode et
se retrouver avec une empreinte mémoire énorme. Nous perdons
complétement l'intérêt de la POO d'un point de vue éxectution.
Javascript va plutôt se baser sur un type d'objet particulier nommés
prototype.</p>
<p><strong>Conclusion</strong> : nous avons fait de la merde, nous allons faire
autrement.</p>
<h2>Dernier point avant de tout reprendre du début</h2>
<p>Avant de voir cette notion de <em>prototype</em>, nous allons voir un petit
piège. Considérons le code suivant où plutôt que de définir nos méthodes
à l'intérieur de notre classe, on le fait après, à l'extérieur :
rappelez vous, Javascript permet de redéfinir les propriétés de ses
objets.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* attributs */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* méthodes déclarées à l'extérieur */</span>
<span class="nx">ClasseA</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">ClasseA</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Exécutons notre appel précédent</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">moninstance</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">(</span><span class="mf">3</span><span class="p">,</span><span class="mf">2</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">moninstance</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="ne">TypeError</span><span class="o">:</span> <span class="nx">moninstance</span><span class="p">.</span><span class="nx">somme</span> <span class="nx">is</span> <span class="nx">not</span> <span class="nx">a</span> <span class="kd">function</span>
</code></pre></div>
<p>Nous avons une erreur et c'est normal. Le <em>new ClasseA</em> ne s'applique
qu'à ce qui est définie dans le constructeur <em>ClasseA</em>. Les méthodes
<em>somme</em> et <em>diff</em> ne sont pas accessibles dans notre objet <em>moninstance</em>
car elles n'y sont pas déclarées. Elles ne sont pas des <strong>méthodes
d'instances</strong> mais des <strong>méthodes de classes</strong>. Nous pouvons appeler ça
aussi des <strong>méthodes statiques</strong>. La seule façon d'y accéder serait de
faire :</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="nx">ClasseA</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="kc">NaN</span>
</code></pre></div>
<p>Le <em>this</em> à l'intérieur de chaque méthode ne correspondrait qu'à la
méthode elle-même. Nous sommes bien face à une <strong>méthode statique</strong>.</p>
<p>::: {.warning}
::: {.admonition-title}
Warning
:::</p>
<p>Toute méthode d'instance doit être définie dans le constructeur via
l'opérateur <em>this</em>. Dans le cas contraire, ça devient une <strong>méthode
statique</strong> (ou <strong>méthode de classe</strong>) et l'opérateur <em>this</em> est sans
effet.
:::</p>
<h2>Les prototypes</h2>
<p>Un prototype est une sorte de \"patron\" qu'un objet va pouvoir
utiliser. C'est la façon de dire à Javascript \"attention, si tu créés
plusieurs instances du même type d'objet (classe), les méthodes ne sont
pas à redéfinir car elles existent dans un patron de référence\". Ne pas
confondre ça avec des Interfaces, car contrairement à ces dernières,
nous pouvons définir la méthode complétement au sein de notre prototype,
là où avec les interfaces, les méthodes ne sont pas définies. (pour
rappel, une interface ne contient que des méthodes abstraites).</p>
<p>Notre exemple précédent</p>
<div class="highlight"><pre><span></span><code><span class="cm">/* constructeur de ClasseA */</span>
<span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* attributs */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span>
<span class="cm">/* methodes */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>deviendra donc</p>
<div class="highlight"><pre><span></span><code><span class="cm">/* constructeur de ClasseA */</span>
<span class="kd">var</span> <span class="nx">ClasseA</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* attributs */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">=</span> <span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* methodes de ClasseA */</span>
<span class="nx">ClasseA</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">somme</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">ClasseA</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Qu'avons nous fait de différent ? La super classe <em>Object</em> dont hérite
tous les objets en Javascript possède comme propriété un objet
<em>prototype</em>. Une fonction (<em>Function</em>) hérite d'<em>Object</em> et donc de sa
propriété <em>prototype</em> aussi. Chaque objet aura donc un objet <em>prototype</em>
faisant partie de ses propriétés. Quand Javascript cherchera la
propriété d'un objet, il ira d'abord la chercher dans l'objet
lui-même (via <em>this</em>) et s'il ne la trouve pas, ira la chercher dans le
prototype. Si ce prototype n'est pas défini, Javascript ira chercher le
prototype de l'objet parent, jusqu'à remonter au prototype de la super
classe <em>Object</em>.</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">instance</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">(</span><span class="mf">2</span><span class="p">,</span><span class="mf">3</span><span class="p">);</span>
<span class="o">>>></span> <span class="kd">var</span> <span class="nx">instance2</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">(</span><span class="mf">5</span><span class="p">,</span><span class="mf">2</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">instance</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="mf">5</span>
<span class="o">>>></span> <span class="nx">instance2</span><span class="p">.</span><span class="nx">somme</span><span class="p">()</span>
<span class="mf">7</span>
</code></pre></div>
<p>On retrouve le même comportement que tout à l'heure, sauf que dans ce
cas, la méthode somme est issue d'un unique et même \"patron\". Nous
avons en mémoire quelque chose qui se rapproche plus d'une classe
classique et nous pouvons désormais créer moultes objets <em>ClasseA</em> avec
une empreinte mémoire et un <a href="http://sebastien-dupire.info/comparer-les-performances-de-structures-objets-en-javascript.html">temps d'éxecution plus faible
qu'avant</a>.</p>
<h2>Héritage en Javascript</h2>
<p>Comme on l'a vu avec l'explication du prototype, quand nous appellons
la propriété d'un objet, Javascript va chercher si l'objet contient
cette méthode. Si ce n'est pas le cas, Javascript va vérifier si elle
est disponible dans le prototype du type correspondant (sa classe), et
si ce n'est pas le cas, Javascript va remonter progressivement
jusqu'au prototype de la super classe <em>Object</em>. C'est une mécanique
d'héritage de prototype.</p>
<p>Nous allons créer un objet <em>ClasseB</em> qui va hériter des propriétés de
<em>ClasseA</em>, il redéfinira la méthode <em>diff</em>, et ajoutera la méthode
<em>multiplie</em></p>
<div class="highlight"><pre><span></span><code><span class="cm">/* ClasseB hérite de ClasseA */</span>
<span class="kd">var</span> <span class="nx">ClasseB</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">ClasseA</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">);</span> <span class="cm">/* Appel du constructeur de la classe Mère */</span>
<span class="p">}</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseA</span><span class="p">();</span>
<span class="cm">/* repointage du constructeur de ClasseB qui était devenu celui de ClasseA par l'instruction précédente */</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="kr">constructor</span> <span class="o">=</span> <span class="nx">ClasseB</span><span class="p">;</span>
<span class="cm">/* methode diff surchargée */</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">diff</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o"><</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrB</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrA</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/* méthode multiplie déclarée */</span>
<span class="nx">ClasseB</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">multiplie</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">attrA</span> <span class="o">*</span> <span class="k">this</span><span class="p">.</span><span class="nx">attrB</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Ce qui donne à l'usage</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="nx">obj</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">ClasseB</span><span class="p">(</span><span class="mf">3</span><span class="p">,</span><span class="mf">6</span><span class="p">)</span>
<span class="nb">Object</span> <span class="p">{</span> <span class="nx">attrA</span><span class="o">=</span><span class="mf">3</span><span class="p">,</span> <span class="nx">attrB</span><span class="o">=</span><span class="mf">6</span><span class="p">,</span> <span class="kr">constructor</span><span class="o">=</span><span class="kd">function</span><span class="p">(),</span> <span class="nx">more</span><span class="p">...}</span>
<span class="o">>>></span> <span class="nx">obj</span><span class="p">.</span><span class="nx">diff</span><span class="p">();</span>
<span class="mf">3</span>
<span class="o">>>></span> <span class="nx">obj</span><span class="p">.</span><span class="nx">multiplie</span><span class="p">();</span>
<span class="mf">18</span>
<span class="o">>>></span> <span class="nx">obj</span><span class="p">.</span><span class="nx">somme</span><span class="p">();</span>
<span class="mf">9</span>
</code></pre></div>
<p>Nous avons bien le résultat attendu. La méthode <em>diff</em> a bien été
redéfinie et la méthode <em>multiplie</em> ajoutée. Notre fille hérite bien des
propriétés de sa classe mère.</p>
<h2>Facile .. oui mais !</h2>
<p>Je vais quand même montrer qu'il peut y avoir des couacs liés au
langage Javascript dans le cas de l'utilisation des prototypes. Pour
cet exemple tordu, je vais rependre le <a href="http://www.coolpage.com/developer/javascript/Correct%20OOP%20for%20Javascript.html">billet trouvé
ici</a>
et qui parle d'une erreur de conception trouvée sur <a href="http://phrogz.net/JS/Classes/OOPinJS2.html">ce billet
là</a>.</p>
<p>Le code d'origine est le suivant (version francisée de l'exemple
trouvé dans les billets cités ci-dessus)</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Classe Animal</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">Animal</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nom</span><span class="p">){</span> <span class="cm">/* Constructeur de la classe Animaux */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">nom</span><span class="o">=</span><span class="nx">nom</span><span class="p">;</span> <span class="cm">/* On définit son nom */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">enfants</span><span class="o">=</span><span class="p">[];</span> <span class="cm">/* On définit un tableau contenant ses enfants */</span>
<span class="p">}</span>
<span class="nx">Animal</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">nouveauBebe</span><span class="o">=</span><span class="kd">function</span><span class="p">(){</span> <span class="cm">/* permet d'ajouter un bébé */</span>
<span class="kd">var</span> <span class="nx">bebe</span><span class="o">=</span><span class="ow">new</span> <span class="nx">Animal</span><span class="p">(</span><span class="s2">"Bebe "</span><span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">nom</span><span class="p">);</span> <span class="cm">/* nouveau bébé */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">enfants</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">bebe</span><span class="p">);</span> <span class="cm">/* qu'on ajoute aux enfants */</span>
<span class="p">}</span>
<span class="nx">Animal</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="o">=</span><span class="kd">function</span><span class="p">(){</span>
<span class="k">return</span> <span class="s1">'[Animal "'</span><span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">nom</span><span class="o">+</span><span class="s1">'"]'</span><span class="p">;</span> <span class="cm">/* affichage du nom */</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * Classe Chat</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">Chat</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nom</span><span class="p">){</span> <span class="cm">/* Constructeur de la classe Chat */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">nom</span><span class="o">=</span><span class="nx">nom</span><span class="p">;</span> <span class="cm">/* On définit son nom */</span>
<span class="p">}</span>
<span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Animal</span><span class="p">();</span>
<span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="kr">constructor</span> <span class="o">=</span> <span class="nx">Chat</span><span class="p">;</span>
</code></pre></div>
<p>Maintenant que nos classes sont déclarées, nous allons les utiliser</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">monanimal</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Animal</span><span class="p">(</span><span class="s2">"Papa"</span><span class="p">);</span>
<span class="o">>>></span> <span class="kd">var</span> <span class="nx">monchat</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Chat</span><span class="p">(</span><span class="s2">"Felix"</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">monanimal</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="s2">"[Animal "</span><span class="nx">Papa</span><span class="s2">"]"</span>
<span class="o">>>></span> <span class="nx">monchat</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="s2">"[Chat "</span><span class="nx">Felix</span><span class="s2">"]"</span>
<span class="o">>>></span> <span class="nx">monchat</span><span class="p">.</span><span class="nx">nouveauBebe</span><span class="p">();</span>
<span class="o">>>></span> <span class="nx">monchat</span><span class="p">.</span><span class="nx">enfants</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
<span class="mf">1</span>
</code></pre></div>
<p>Tout va bien, mais si on ajoute</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">monchat2</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Chat</span><span class="p">(</span><span class="s2">"Ronron"</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">monchat2</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="s2">"[Chat "</span><span class="nx">Ronron</span><span class="s2">"]"</span>
<span class="o">>>></span> <span class="nx">monchat2</span><span class="p">.</span><span class="nx">nouveauBebe</span><span class="p">();</span>
<span class="o">>>></span> <span class="nx">monchat2</span><span class="p">.</span><span class="nx">enfants</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="cm">/* pas cool */</span>
<span class="mf">2</span>
<span class="o">>>></span> <span class="nx">monchat2</span><span class="p">.</span><span class="nx">enfants</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span> <span class="cm">/* Celui là appartient à Felix, pas à Ronron */</span>
<span class="p">[</span><span class="nx">Animal</span> <span class="s2">"Bebe Felix"</span><span class="p">]</span> <span class="p">{</span> <span class="nx">nom</span><span class="o">=</span><span class="s2">"Bebe Felix"</span><span class="p">,</span> <span class="nx">enfants</span><span class="o">=</span><span class="p">[</span><span class="mf">0</span><span class="p">],</span> <span class="nx">nouveauBebe</span><span class="o">=</span><span class="kd">function</span><span class="p">(),</span> <span class="nx">more</span><span class="p">...}</span>
<span class="o">>>></span> <span class="nx">monchat2</span><span class="p">.</span><span class="nx">enfants</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span> <span class="cm">/* Celui là a été ajouté alors qu'il aurait du être tout seul */</span>
<span class="p">[</span><span class="nx">Animal</span> <span class="s2">"Bebe Ronron"</span><span class="p">]</span> <span class="p">{</span> <span class="nx">nom</span><span class="o">=</span><span class="s2">"Bebe Ronron"</span><span class="p">,</span> <span class="nx">enfants</span><span class="o">=</span><span class="p">[</span><span class="mf">0</span><span class="p">],</span> <span class="nx">nouveauBebe</span><span class="o">=</span><span class="kd">function</span><span class="p">(),</span> <span class="nx">more</span><span class="p">...}</span>
<span class="o">>>></span> <span class="nx">monchat</span><span class="p">.</span><span class="nx">enfants</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="cm">/* En ajoutant un enfant à monchat2, monchat se le voit attribuer aussi */</span>
<span class="mf">2</span>
</code></pre></div>
<p>Nous avons une erreur : la propriété <em>enfants</em> à l'air d'être commune
à mes deux objets filles que sont <em>monchat</em> et <em>monchat2</em>. Normalement,
<em>monchat2</em> n'aurait du avoir qu'un seul enfant à l'ajout d'un
enfant. Or, il avait déjà en mémoire l'enfant de <em>monchat</em></p>
<p>Pourquoi cela ?</p>
<p>Nous avions dit à l'introduction des prototypes que cela agissait comme
une sorte de \"patron\" commun à l'ensemble des objets instanciés.</p>
<p>Reprenons l'exemple, on commmentant le prototype.constructor</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Classe Chat</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">Chat</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nom</span><span class="p">){</span> <span class="cm">/* Constructeur de la classe Chat */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">nom</span><span class="o">=</span><span class="nx">nom</span><span class="p">;</span> <span class="cm">/* On définit son nom */</span>
<span class="p">}</span>
<span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Animal</span><span class="p">();</span>
<span class="cm">/* Chat.prototype.constructor = Chat; */</span>
</code></pre></div>
<p>A ce moment précis, le prototype du <em>Chat</em> devient celui d'un objet
<em>Animal</em> et cette opération n'est faite qu'une seule fois durant toute
l'éxecution du script.</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span>
<span class="p">[</span><span class="nx">Chat</span> <span class="s2">"undefined"</span><span class="p">]</span> <span class="p">{</span> <span class="nx">enfants</span><span class="o">=</span><span class="p">[</span><span class="mf">0</span><span class="p">],</span> <span class="nx">toString</span><span class="o">=</span><span class="kd">function</span><span class="p">(),</span> <span class="nx">nouveauBebe</span><span class="o">=</span><span class="kd">function</span><span class="p">()}</span>
</code></pre></div>
<p>Le prototype de <em>Chat</em> contient comme attribut la liste <em>enfants</em> et
<em>nom</em> n'est pas défini (undefined). Le constructeur de A n'initialise
<em>nom</em> qu'à la création de l'objet mais une fois qu'on indique que la
classe <em>Chat</em> aura comme prototype un objet de type <em>Animal</em> le
constructeur de <em>Chat</em> devient celui de l'objet <em>Animal</em>, ce qui nous
arrange pas.</p>
<p>Pour changer cela, nous faisons en sorte que le <em>prototype.constructor</em>
de <em>Chat</em> devienne la fonction <em>Chat</em> (au lieu de l'objet <em>Animal</em>).
Toutefois, dans ce constructeur, nous ne faisons que traiter l'attribut
<em>nom</em>. Du coup, <em>enfants</em> existe bien pour un objet <em>Chat</em> mais fait
parti de son prototype et non de son constructeur.</p>
<p>Aussi, lorsque nous instancions un <em>Chat</em> et lui ajoutons un enfant</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">monchat</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Chat</span><span class="p">(</span><span class="s2">"Felix"</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">monchat</span><span class="p">.</span><span class="nx">nouveauBebe</span><span class="p">();</span>
</code></pre></div>
<ul>
<li><em>nom</em> sera initialisé dans le constructeur de l'objet <em>monchat</em> de
type <em>Chat</em> et deviendra \"Felix\"</li>
<li><em>enfants</em> sera augmenté d'une référence. Mais <em>enfants</em>
n'appartient pas à l'objet <em>monchat</em> mais au prototype de <em>Chat</em></li>
</ul>
<p>Lorsqu'on va instancier un autre <em>Chat</em> et lui ajouter un enfant</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="kd">var</span> <span class="nx">monchat2</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Chat</span><span class="p">(</span><span class="s2">"Ronron"</span><span class="p">);</span>
<span class="o">>>></span> <span class="nx">monchat2</span><span class="p">.</span><span class="nx">nouveauBebe</span><span class="p">();</span>
</code></pre></div>
<ul>
<li><em>nom</em> sera initialisé dans le constructeur de l'objet monchat2 de
type <em>Chat</em> et eviendra \"Ronron\"</li>
<li><em>enfants</em> sera augmenté d'une référence. Mais <em>enfants</em>
n'appartient pas à l'objet <em>monchat</em> mais au prototype de <em>Chat</em>
qui contient déjà une référence.</li>
</ul>
<p>Voilà, j'espère que vous avez compris ! Nous allons bien avoir deux
<em>enfants</em> alors que pour chaque objet, un seul a été ajouté. Durant la
déclaration de <em>Chat</em>, nous lui avons dit que son prototype allait
hériter des propriétés de <em>Animal</em> et ça, nous le faisons QU'UNE SEULE
FOIS durant toute l'éxecution du script au moment de la déclaration du
prototype (nous sommes dans un langage de script ou chaque ligne est
interprétée où elle est lue).</p>
<p>Le paradigme prototype en Javascript a certains inconvénients que seule
la rigueur permettra de combler. En effet, pour que notre classe <em>Chat</em>
soit déclarée correctement, il aurait suffit de remplacer</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Classe Chat</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">Chat</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nom</span><span class="p">){</span> <span class="cm">/* Constructeur de la classe Chat */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">nom</span><span class="o">=</span><span class="nx">nom</span><span class="p">;</span> <span class="cm">/* On définit son nom */</span>
<span class="p">}</span>
<span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Animal</span><span class="p">();</span>
<span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="kr">constructor</span> <span class="o">=</span> <span class="nx">Chat</span><span class="p">;</span>
</code></pre></div>
<p>par le code suivant</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Classe Chat</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">Chat</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nom</span><span class="p">){</span> <span class="cm">/* Constructeur de la classe Chat */</span>
<span class="nx">Animal</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="nx">nom</span><span class="p">);</span> <span class="cm">/* Appel du constructeur de la classe A */</span>
<span class="p">}</span>
<span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Animal</span><span class="p">();</span>
<span class="nx">Chat</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="kr">constructor</span> <span class="o">=</span> <span class="nx">Chat</span><span class="p">;</span>
</code></pre></div>
<p>Dans ce cas, vu que le constructeur va redéfinir <em>nom</em> et <em>enfants</em>
(trouvés dans le constructeur de la classe <em>Animal</em>), Javascript au
moment de chercher l'attribut <em>enfants</em>, ira le chercher dans le
constructeur alors qu'avant le correctif, il allait le chercher dans le
prototype <em>Chat</em>. Javascript n'est pas en tord : ici le problème,
c'est l'interface chaise-clavier (nous).</p>
<h2>Conclusion de ce (très/trop) long billet</h2>
<p>Vous savez maintenant créer des classes en Javascript :</p>
<ul>
<li>soit en définissant une classe par son constructeur : rédaction
simple, mais duplication systématique des méthodes au sein des
objets instanciés.</li>
<li>soit en définissant une classe à l'aide d'un prototype : rédaction
compliquée, erreur de syntaxe à prévoir, demande plus de rigueur,
mais empreinte mémoire plus réduite.</li>
</ul>
<p>Javascript est un langage à part entière. Avant de le critiquer, il faut
s'y intéresser et comprendre sa façon de fonctionner car il permet
énormément de choses.</p>
<p>Maintenant que nous avons toutes ces informations sur la POO, nous
allons pouvoir refaire notre exemple de canvas précédent mais avec un
peu plus de structure. Cela fera l'objet d'un prochain billet.</p>
<h2>Références</h2>
<p>Tous ces liens m'ont permis de récupérer des informations et des
exemples me permettant de rédiger ce billet.</p>
<ul>
<li><a href="http://mckoss.com/jscript/object.htm">http://mckoss.com/jscript/object.htm</a></li>
<li><a href="http://jpvincent.developpez.com/tutoriels/javascript/javascript-oriente-objet-syntaxe-base-classes-js-intention-developpeurs-php/">http://jpvincent.developpez.com/tutoriels/javascript/javascript-oriente-objet-syntaxe-base-classes-js-intention-developpeurs-php/</a></li>
<li>(http://tcorral.github.com/Design-Patterns-in-Javascript/)</li>
<li>(http://javascriptweblog.wordpress.com/2010/03/16/five-ways-to-create-objects/)</li>
<li>(http://www.asp-php.net/tutorial/scripting/javascript-et-poo.php)</li>
<li>(http://jacques-guizol.developpez.com/javascript/Entites/Entites.php)</li>
<li>(http://phrogz.net/js/classes/OOPinJS2.html)</li>
<li>(http://www.coolpage.com/developer/javascript/Correct%20OOP%20for%20Javascript.html)</li>
<li>(http://odetocode.com/blogs/scott/archive/2007/07/05/function-apply-and-function-call-in-javascript.aspx)</li>
<li>(http://classy.pocoo.org/)</li>
<li>(http://jpvincent.developpez.com/tutoriels/javascript/usage-avance-fonctions-javascript/?utm_source=twitterfeed&utm_medium=twitter)</li>
<li>(http://en.wikipedia.org/wiki/Prototype_pattern)</li>
</ul>
<p>Si vous constatez des erreurs et/ou que vous avez des questions,
n'hésitez pas à poster des commentaires. Toute information utile est la
bienvenue pour enrichir ce billet.</p>Jouer avec canvas - épisode 12011-12-14T00:00:00+01:002011-12-14T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-12-14:/javascript-jouer-avec-canvas-episode-1.html<p>C'est bientôt les fêtes de Noël et je vais donc profiter des quelques jours de vacances qui arrivent pour mettre en ligne quelques tutoriels sur l'utilisation de canvas avec le langage de script Javascript.</p>
<h2>L'élement canvas</h2>
<p>L'élément canvas est un composant faisant partie de la spécification
HTML5 qui permet d'effectuer …</p><p>C'est bientôt les fêtes de Noël et je vais donc profiter des quelques jours de vacances qui arrivent pour mettre en ligne quelques tutoriels sur l'utilisation de canvas avec le langage de script Javascript.</p>
<h2>L'élement canvas</h2>
<p>L'élément canvas est un composant faisant partie de la spécification
HTML5 qui permet d'effectuer des rendus bitmap et de les manipuler via
des scripts (la plupart en Javascript).</p>
<p>Il suffit tout simplement d'ajouter la balise suivante dans le body de
notre document HTML.</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">canvas</span> <span class="na">id</span><span class="o">=</span><span class="s">"zone"</span> <span class="na">width</span><span class="o">=</span><span class="s">"800"</span> <span class="na">height</span><span class="o">=</span><span class="s">"400"</span><span class="p">></span>
Texte à afficher si le navigateur ne prend pas en compte l'élement canvas
<span class="p"></</span><span class="nt">canvas</span><span class="p">></span>
</code></pre></div>
<h2>Création d'un petit jeu</h2>
<p>La série des tutoriels à venir portera sur la création d'un petit jeu
type Shoot'em up afin d'aborder plusieurs aspects des animations de la
manipulation des images et des textes dans l'élement canvas via
Javascript. Afin de nous aider dans la gestion des évenements, des
potentielles futures requêtes AJAX et la manipulation du DOM, nous
utiliserons la librairie jQuery mais nous pourrions très bien nous en
passer ou utiliser une autre librairie.</p>
<h2>Objectif du Tutoriel</h2>
<p>Dans ce tutoriel, nous allons mettre en place un avion de chasse vu de
coté qu'on pourra déplacer sur les deux axes via les flèches du
clavier. A la fin, nous ajouterons un évenement clavier afin de faire
trembler la carcasse de notre avion et ajouter un peu de fun dans notre
animation.</p>
<h2>Mise en place du canvas et récupération du contexte</h2>
<p>Tout d'abord, on va créer un fichier html qui contiendra la balise
canvas et le nécessaire pour utiliser nos scripts.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><!DOCTYPE html></span>
<span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"fr"</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"Content-Type"</span> <span class="na">content</span><span class="o">=</span><span class="s">"text/html; charset=utf-8"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>Test Canvas Animation<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/javascript"</span> <span class="na">src</span><span class="o">=</span><span class="s">"jquery-1.7.1.min.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/javascript"</span><span class="p">></span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="c1">// Insérer votre code ici !</span>
<span class="p">});</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span> <span class="na">id</span><span class="o">=</span><span class="s">"precharge"</span><span class="p">></span>Chargement de la démo ... veuillez patienter<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span> <span class="na">id</span><span class="o">=</span><span class="s">"button"</span><span class="p">></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"button"</span> <span class="na">id</span><span class="o">=</span><span class="s">"start"</span> <span class="na">value</span><span class="o">=</span><span class="s">"Start"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"button"</span> <span class="na">id</span><span class="o">=</span><span class="s">"stop"</span> <span class="na">value</span><span class="o">=</span><span class="s">"Stop"</span> <span class="p">/></span>
<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">strong</span><span class="p">></span>UP DOWN LEFT RIGHT<span class="p"></</span><span class="nt">strong</span><span class="p">></span> : se diriger<span class="p"><</span><span class="nt">br</span><span class="p">/></span>
Maintenir <span class="p"><</span><span class="nt">strong</span><span class="p">></span>T<span class="p"></</span><span class="nt">strong</span><span class="p">></span> : actionner le Turbo
<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">canvas</span> <span class="na">id</span><span class="o">=</span><span class="s">"zone"</span> <span class="na">width</span><span class="o">=</span><span class="s">"800"</span> <span class="na">height</span><span class="o">=</span><span class="s">"400"</span><span class="p">></span>
Votre Navigateur ne gère pas canvas .. dommage !!!
<span class="p"></</span><span class="nt">canvas</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>Cette partie du code ne sera plus du tout modifiée. Elle contient un
message à afficher lors du chargement de notre application, d'un bouton
Start, d'un bouton Stop, de consignes pour jouer et notre balise canvas</p>
<h2>Les variables</h2>
<p>La seule chose que nous allons faire maintenant, est d'insérer du code
Javascript là où il est indiqué <em>Insérer votre code ici</em>. Nous
définissons des variables qui seront accessibles depuis toutes les
méthodes de notre application.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/* On récupère du contexte */</span>
<span class="kd">var</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">"#zone"</span><span class="p">);</span> <span class="cm">/* objet jQuery associé au canvas */</span>
<span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mf">0</span><span class="p">).</span><span class="nx">getContext</span><span class="p">(</span><span class="s2">"2d"</span><span class="p">);</span> <span class="cm">/* contexte du canvas */</span>
<span class="cm">/* On récupère les dimensions du canvas */</span>
<span class="kd">var</span> <span class="nx">canvasWidth</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">canvasHeight</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">();</span>
<span class="cm">/* On définit les touches à utiliser dans notre jeu */</span>
<span class="kd">var</span> <span class="nx">arrowLeft</span> <span class="o">=</span> <span class="mf">37</span><span class="p">;</span> <span class="cm">/* LEFT */</span>
<span class="kd">var</span> <span class="nx">arrowRight</span> <span class="o">=</span> <span class="mf">39</span><span class="p">;</span> <span class="cm">/* RIGHT */</span>
<span class="kd">var</span> <span class="nx">arrowUp</span> <span class="o">=</span> <span class="mf">38</span><span class="p">;</span> <span class="cm">/* UP */</span>
<span class="kd">var</span> <span class="nx">arrowDown</span> <span class="o">=</span> <span class="mf">40</span><span class="p">;</span> <span class="cm">/* DOWN */</span>
<span class="kd">var</span> <span class="nx">turboKey</span> <span class="o">=</span> <span class="mf">84</span><span class="p">;</span> <span class="cm">/* T */</span>
<span class="cm">/* Tag sur les déplacements */</span>
<span class="kd">var</span> <span class="nx">goUp</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">goDown</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">goRight</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">goLeft</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">turbo</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="cm">/* Position de notre avion */</span>
<span class="kd">var</span> <span class="nx">posX</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">posY</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
</code></pre></div>
<p>On en profite pour ajouter une méthode permettant de rétablir la plupart
de ces paramètres.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Methode de réinitialisation des variables.</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">reset</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">goUp</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">goDown</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">goRight</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">goLeft</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">turbo</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">posX</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
<span class="nx">posY</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Les commentaires parlent d'eux-mêmes :</p>
<ul>
<li>nous récupèrons notre canvas, son contexte et quelques informations
sur lui (sa taille).</li>
<li>nous définissons des KeyCode clavier ce qui nous permetra de pouvoir
changer la configuration des touches de notre jeu. (<a href="http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes">Voir les
KeyCode
ici</a>)</li>
<li>nous définissons des flags sur les mouvements que notre avion est
susceptible de faire</li>
<li>nous enregistrons la position de l'avion durant toute la session de
jeu</li>
</ul>
<h2>Les évenements clavier</h2>
<p>Ensuite, on va définir des évenements sur chacune de nos touches aussi
bien au moment de l'appui qu'au moment du relachement.</p>
<div class="highlight"><pre><span></span><code><span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">).</span><span class="nx">keydown</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowUp</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Mon avion doit monter */</span>
<span class="nx">goUp</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">goDown</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowDown</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Mon avion doit descendre */</span>
<span class="nx">goUp</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">goDown</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowLeft</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Mon avion va à gauche */</span>
<span class="nx">goLeft</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">goRight</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowRight</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Mon avion va à droite */</span>
<span class="nx">goLeft</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">goRight</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">turboKey</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Mon avion active son turbo */</span>
<span class="nx">turbo</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">).</span><span class="nx">keyup</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowUp</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">goUp</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowDown</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">goDown</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowLeft</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">goLeft</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">arrowRight</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">goRight</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">==</span> <span class="nx">turboKey</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">turbo</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div>
<p>L'animation consistant en une boucle infinie, nous allons uniquement
modifier des états avec nos évenements. C'est la boucle infine qui
éxecutera l'action à faire en fonction des états définis.</p>
<h2>Chargement des élements</h2>
<p><img alt="Notre avion de 200x95 pixels" src="https://sebastien-dupire.info/pictures/0005-avion_200_95_1.png"></p>
<p>On définit ensuite notre avion qui est disponible sous la forme d'un
PNG à fond transparent. Par contre, vu qu'il peut mettre un certain
temps à être chargé par le navigateur, il faudra s'assurer qu'il soit
bien complet avant de jouer avec notre image dans le canvas.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/* On affiche le message de chargement, on masque les boutons Start et Stop */</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#precharge"</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">hide</span><span class="p">();</span>
<span class="cm">/* On charge l'image de notre avion... */</span>
<span class="kd">var</span> <span class="nx">avion</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">Image</span><span class="p">();</span>
<span class="nx">avion</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">"avion_200_95_1.png"</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">avion</span><span class="p">).</span><span class="nx">load</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="cm">/* ... et fois l'image en mémoire, on remet en place le bouton Start</span>
<span class="cm"> et le bouton Stop et on masque le loading */</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#precharge"</span><span class="p">).</span><span class="nx">hide</span><span class="p">();</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#button"</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
<span class="p">});</span>
</code></pre></div>
<h2>La méthode de rendu</h2>
<p>Pour afficher une nouvelle frame, il suffit de la redessiner
complètement. On pourrait utiliser d'autres techniques comme effacer
l'avion du canvas et le remplacer par le morceau de background qu'il a
remplacé, et le redessiner ailleurs mais cette fois ci, on fera au plus
simple.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderFrame</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span> <span class="cm">/* On efface le canvas */</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="s2">"rgb(200, 200, 200)"</span><span class="p">;</span> <span class="cm">/* On définit la couleur de fond */</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span> <span class="cm">/* On créé un rectangle plein de la taille du canevas*/</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">avion</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">,</span> <span class="nx">posX</span><span class="p">,</span> <span class="nx">posY</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">);</span> <span class="cm">/* On affiche l'avion à sa nouvelle position */</span>
<span class="p">}</span>
</code></pre></div>
<h2>Controles de la boucle infinie</h2>
<p>Bien entendu, il va falloir lancer cette méthode régulièrement. Nous
allons donc utiliser la fonction
<a href="https://developer.mozilla.org/fr/DOM/window.setInterval">setInterval</a>
qui permet de réaliser une action tous les X millisecondes sur une
méthode qui lancera notre <em>renderFrame</em></p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">animationFlag</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">delayBetweenFrames</span> <span class="o">=</span> <span class="mf">25</span> <span class="cm">/* ms */</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#start"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">reset</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">animationFlag</span><span class="o">==</span><span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">animationFlag</span> <span class="o">=</span> <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">mainLoop</span><span class="p">();</span>
<span class="p">},</span> <span class="nx">delayBetweenFrames</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#stop"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">reset</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">animationFlag</span><span class="o">!=</span><span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">animationFlag</span><span class="p">);</span>
<span class="nx">animationFlag</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="cm">/**</span>
<span class="cm"> * Main LOOP</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">mainLoop</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">renderFrame</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>
<h2>Problèmes de frames</h2>
<p>Si vous avez bien suivi le code précédent, la donnée importante, c'est
le paramètre <em>delayBetweenFrames</em> (25ms) de la fonction
<a href="https://developer.mozilla.org/fr/DOM/window.setInterval">setInterval</a>.
Cela veut dire que toutes les 25ms une nouvelle image sera calculée. Ce
qui correspond à un 40 images / secondes (ce qui est largement suffisant
voir même trop).</p>
<p>Un problème se pose : que faisons nous si le traitement dure plus de
25ms ? En effet, admettons que les traitement pour calculer la position
soit trop long et qu'on demande à notre fonction de redessiner le
canvas alors qu'elle n'avait pas fini la précédente. Ca risque de
générer des soucis d'affichage.</p>
<p>Pour cela, on va ajouter un flag afin de savoir si la frame précédente
est toujours en cours de rendu. Si un tel cas se produit, nous ne
lancerons pas la frame suivante, mais nous la ferons au prochaine 25ms.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span> <span class="cm">/* Flag pour savoir si une Frame est en cours de construction */</span>
</code></pre></div>
<p>Nous ajoutons le changement d'état durant la réalisation d'une frame</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderFrame</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="cm">/* On vérouille la frame */</span>
<span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="cm">/* Dessin de la nouvelle Frame */</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="s2">"rgb(200, 200, 200)"</span><span class="p">;</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">avion</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">,</span> <span class="nx">posX</span><span class="p">,</span> <span class="nx">posY</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">);</span>
<span class="cm">/* Dévérouillage de la frame */</span>
<span class="nx">animationActiveFrame</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Enfin, nous modifions notre boucle infinie</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Main LOOP</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">mainLoop</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">animationActiveFrame</span><span class="o">==</span><span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">renderFrame</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<h2>Calcul de la position de l'avion</h2>
<p>Bien evidemment, l'animation peut se lancer mais notre avion ne bouge
toujours pas. Il va falloir indiquer à notre boucle qu'à chaque
itération, elle doit calculer la position de l'avion en fonction des
mouvements détectés par les changements d'états. Quoiqu'il arrive,
nous devons executer cette méthode toutes durant le <em>delayBetweenFrames</em>
(25ms)</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">calculPositions</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="cm">/* Calcul des nouvelles coordonées */</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goUp</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goDown</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goLeft</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goRight</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>et notre boucle devient :</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">mainLoop</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">calculPositions</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">animationActiveFrame</span><span class="o">==</span><span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">renderFrame</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Nous allons déplacer notre image de 3 pixels en fonction de la direction
choisie toutes les 25ms. Cela va représenter 40 frames/sec x 3 pixels =
120 pixels par seconde de déplacement.</p>
<h2>Premier test</h2>
<p>Normalement, à ce stade, votre animation doit être fonctionnelle.
Appuyez sur START et déplacez vous dans le canvas pour observer le
résultat. Si vous n'avez rien foutu durant le tutoriel, <a href="http://kyoku57.org/demo/animations-canvas/demo_00/">allez voir
ici</a>.</p>
<h2>Pour le fun : ajout du Turbo</h2>
<p>Nous aimerions faire en sorte que notre avion tremble de manière
progressive quand nous appuyons sur la touche T.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">rX</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="cm">/* Décalage sur X */</span>
<span class="kd">var</span> <span class="nx">rY</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="cm">/* Décalage sur Y */</span>
<span class="kd">var</span> <span class="nx">rI</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">;</span> <span class="cm">/* Pixel de décalage aléatoire */</span>
<span class="kd">var</span> <span class="nx">rIMax</span> <span class="o">=</span> <span class="mf">10.0</span><span class="p">;</span> <span class="cm">/* Pixel de décalage Max */</span>
<span class="kd">var</span> <span class="nx">rIAcc</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">;</span> <span class="cm">/* offset à chaque itération */</span>
</code></pre></div>
<p>Nous allons calculer le décalage supplémentaire en X et Y à appliquer
pour chaque frame</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">calculPositions</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="cm">/* Calcul des nouvelles coordonées */</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goUp</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goDown</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posY</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goLeft</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">-=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">goRight</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">posX</span> <span class="o">+=</span> <span class="mf">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* Calcul des turbulences */</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">turbo</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">rI</span> <span class="o"><</span> <span class="nx">rIMax</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">rI</span> <span class="o">=</span> <span class="nx">rI</span> <span class="o">+</span> <span class="nx">rIAcc</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">rI</span> <span class="o">></span> <span class="mf">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">rI</span> <span class="o">=</span> <span class="nx">rI</span> <span class="o">-</span> <span class="nx">rIAcc</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">rX</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="nx">rI</span><span class="p">)</span><span class="o">-</span><span class="nx">rI</span><span class="o">/</span><span class="mf">2.0</span><span class="p">);</span>
<span class="nx">rY</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="nx">rI</span><span class="p">)</span><span class="o">-</span><span class="nx">rI</span><span class="o">/</span><span class="mf">2.0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>Dans <em>renderFrame</em>, nous remplaçons</p>
<div class="highlight"><pre><span></span><code><span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">avion</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">,</span> <span class="nx">posX</span><span class="p">,</span> <span class="nx">posY</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">);</span>
</code></pre></div>
<p>par</p>
<div class="highlight"><pre><span></span><code><span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">avion</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">,</span> <span class="nx">posX</span><span class="o">+</span><span class="nx">rX</span><span class="p">,</span> <span class="nx">posY</span><span class="o">+</span><span class="nx">rY</span><span class="p">,</span> <span class="mf">200</span><span class="p">,</span> <span class="mf">95</span><span class="p">);</span>
</code></pre></div>
<p>Désormais un décalage aléatoire en X et Y a lieu et il est d'autant
plus important que la touche T est pressée longtemps.</p>
<h2>Une histoire de performance</h2>
<p>Tout le problème de l'animation telle qu'on la réalise dans ce
tutoriel, c'est qu'on part du principe qu'une image a toujours le
temps de se construire dans les temps. Ce n'est pas aussi simple, et la
rapidité d'execution va dépendre de la machine et du moteur javascript
du navigateur. Dans notre cas, la seule chose importante est que le
temps de calcul et de rendu soient inférieurs à <em>delayBetweenFrames</em>
(25ms)</p>
<h3>Ajout du monitoring</h3>
<p>Afin de pouvoir tracer les temps d'execution, nous pouvons nous faire
un log maison à la rache (de loin mes préférés !!).</p>
<p>Nous ajoutons une div dans notre fichier html</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">"debug"</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>Fréquence : <span class="p"><</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">"debug_frame"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span> frames/sec<span class="p"><</span><span class="nt">br</span><span class="p">/></span>
Temps execution : <span class="p"><</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">"debug_execution"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span> ms<span class="err"><</span>/br/>
Temps de rendu : <span class="p"><</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">"debug_render"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span> ms<span class="p"><</span><span class="nt">br</span><span class="p">/></span>
Temps total par frame : <span class="p"><</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">"debug_total"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span> ms<span class="p"><</span><span class="nt">br</span><span class="p">/></span>
Référence : <span class="p"><</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">"debug_reference"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span> ms<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
</code></pre></div>
<p>Nous ajoutons le code qui nous servira à l'analyse des temps</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Variables de Debuggage</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">exec_time</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="cm">/* Temps d'execution */</span>
<span class="kd">var</span> <span class="nx">render_time</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="cm">/* Temps de rendu */</span>
<span class="kd">var</span> <span class="nx">frame_count</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="cm">/* Compteur de frame */</span>
<span class="kd">var</span> <span class="nx">timeref</span> <span class="o">=</span> <span class="p">(</span><span class="ow">new</span> <span class="nb">Date</span><span class="p">()).</span><span class="nx">getTime</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">debugFlag</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="cm">/* Nécessaire pour interrompre le setInterval */</span>
<span class="cm">/**</span>
<span class="cm"> * Méthode pour déterminer le temps passer entre deux appels de la méthode </span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">debug_gap</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="kd">var</span> <span class="nx">new_time</span> <span class="o">=</span> <span class="p">(</span><span class="ow">new</span> <span class="nb">Date</span><span class="p">()).</span><span class="nx">getTime</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">diff</span> <span class="o">=</span> <span class="nx">new_time</span> <span class="o">-</span> <span class="nx">timeref</span><span class="p">;</span>
<span class="nx">timeref</span> <span class="o">=</span> <span class="nx">new_time</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">diff</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**</span>
<span class="cm"> * Affichage des informations de débugage</span>
<span class="cm"> */</span>
<span class="kd">function</span> <span class="nx">showDebug</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#debug_frame"</span><span class="p">).</span><span class="nx">get</span><span class="p">(</span><span class="mf">0</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">frame_count</span><span class="p">;</span>
<span class="nx">frame_count</span> <span class="o">=</span> <span class="mf">0</span><span class="p">;</span> <span class="cm">/* On réinitialise le compteur pour 1 seconde*/</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#debug_execution"</span><span class="p">).</span><span class="nx">get</span><span class="p">(</span><span class="mf">0</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">exec_time</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#debug_render"</span><span class="p">).</span><span class="nx">get</span><span class="p">(</span><span class="mf">0</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">render_time</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#debug_total"</span><span class="p">).</span><span class="nx">get</span><span class="p">(</span><span class="mf">0</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">render_time</span> <span class="o">+</span> <span class="nx">exec_time</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#debug_reference"</span><span class="p">).</span><span class="nx">get</span><span class="p">(</span><span class="mf">0</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">delayBetweenFrames</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Nous allons ajouter les méthodes dans les fonctions à mesurer</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">renderFrame</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">frame_count</span><span class="o">++</span><span class="p">;</span> <span class="cm">/* On compte le nombre de frames durant 1 secondes */</span>
<span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* Initialisation du temps de calcul */</span>
<span class="p">....</span>
<span class="nx">render_time</span> <span class="o">=</span> <span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* Calcul du temps de rendu */</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">calculPositions</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* Initialisation du temps de rendu */</span>
<span class="p">....</span>
<span class="c1">//for(var i=0; i<400000; i++) {} /* Facultatif : permet juste d'allonger artificiellement le tps d'execution */</span>
<span class="nx">exec_time</span> <span class="o">=</span> <span class="nx">debug_gap</span><span class="p">();</span> <span class="cm">/* Calcul du temps de rendu */</span>
<span class="p">}</span>
</code></pre></div>
<p>Nous allons modifier les évenements START et STOP afin de gérer le
debug, qui rafraichira l'affichage des informations toutes les
secondes.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * Evenements sur bouton START ET STOP</span>
<span class="cm"> */</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#start"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">reset</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">animationFlag</span><span class="o">==</span><span class="kc">null</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Création de la boucle infinie*/</span>
<span class="nx">animationFlag</span> <span class="o">=</span> <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">mainLoop</span><span class="p">();</span>
<span class="p">},</span><span class="nx">delayBetweenFrames</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">debugFlag</span><span class="o">==</span><span class="kc">null</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Debug */</span>
<span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">showDebug</span><span class="p">();</span>
<span class="p">},</span><span class="mf">1000</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">"#stop"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">animationFlag</span><span class="o">!=</span><span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* Arrêt de la boucle infinie et effacement du canvas */</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">animationFlag</span><span class="p">);</span>
<span class="nx">animationFlag</span><span class="o">=</span><span class="kc">null</span><span class="p">;</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">0</span><span class="p">,</span> <span class="nx">canvasWidth</span><span class="p">,</span> <span class="nx">canvasHeight</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">debugFlag</span><span class="o">!=</span><span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* Remise à zéro du mode debug */</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">debugFlag</span><span class="p">);</span>
<span class="nx">debugFlag</span><span class="o">=</span><span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">reset</span><span class="p">();</span>
<span class="p">});</span>
</code></pre></div>
<h3>Interprétation des tests</h3>
<p>Test réalisé sous Firefox 8.0 sous GNU/Linux (avec accélération
graphique) Processeur : Core 2 Duo E8400 + 4 Go de RAM + Radeon HD4870</p>
<div class="highlight"><pre><span></span><code>Fréquence : <span class="m">40</span> frames/sec
Temps execution : <span class="m">0</span> ms
Temps de rendu : <span class="m">0</span> ms
Temps total par frame : <span class="m">0</span> ms
Référence : <span class="m">25</span> ms
</code></pre></div>
<p>On constate qu'on a bien les 40 frames par seconde et que les temps d'execution et de rendu sont inférieurs à la milliseconde. Notre animation se déroule dans le cadre idéal et on peut constater au lancement de l'animation que notre avion se déplace de manière très fluide.</p>
<p>Test réalisé sous un Android 2.1 - Sony Ericsson X10 mini pro</p>
<div class="highlight"><pre><span></span><code>Fréquence : <span class="m">8</span> frames/sec
Temps execution : <span class="m">127</span> ms
Temps de rendu : <span class="m">2</span> ms
Temps total par frame : <span class="m">129</span> ms
Référence : <span class="m">25</span> ms
</code></pre></div>
<p>Là par contre, c'est un peu plus problématique. Nous avons environ qu'une frame sur cinq de réalisée à cause d'un temps de calcul des positions trop long. Il faudra soit augmenter la référence (délai entre deux frames), soit penser le système de calcul et de rendu autrement. Ce qui est étonnant c'est que le rendu ne prend finalement pas beaucoup de temps par rapport à l'éxecution. A titre personnel, j'aurais cru le contraire.</p>
<h2>Demo Live et code source</h2>
<p>La démo qui utilise le code vu durant ce tutoriel est <a href="http://kyoku57.org/demo/animations-canvas/demo_00/">disponible ici</a>.</p>
<p>Le code source de cette démo est
<a href="https://bitbucket.org/kyoku57/animation-canvas-demos/src/">ici</a>. (dépôt Mercurial)</p>
<h2>Prochains tutoriels à venir</h2>
<ul>
<li>Création d'animations du sprite en fonction des actions : montée, descente, turbo</li>
<li>Refactoring de la démo et modélisation de l'animation</li>
</ul>
<h2>Références</h2>
<ul>
<li><a href="http://fr.wikipedia.org/wiki/Canvas_(HTML)">Page Wikipedia sur l'élement canvas</a></li>
<li><a href="https://developer.mozilla.org/fr/HTML/Canvas">Canvas sur MDN</a></li>
</ul>Twilight 5 : Education2011-12-02T00:00:00+01:002011-12-02T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-12-02:/twilight-5-education.html<p><img alt="image" class="aligncenter" height="676" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-034/twilight_5.jpg" width="624"></p>Twilight 4 : le retour !2011-12-01T00:00:00+01:002011-12-01T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-12-01:/twilight-4-le-retour.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-034/twilight_4.jpg"></p>Réglage du jeu aux soupapes sur GPZ 500 S2011-11-20T19:35:00+01:002011-11-20T19:35:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-11-20:/reglage-du-jeu-aux-soupapes-sur-gpz-500-s.html<p>Après plusieurs déboires, j'ai enfin pu finir l'entretien de ma Kawasaki GPZ 500 S. Le but était de régler le jeu aux soupapes, entretien qui se fait tous les 12 000 kms et qui, par chance est assez facile sur ce modèle.</p>
<iframe src="https://www.youtube.com/embed/ehj21FL3new" frameborder="0" width="624" height="468"></iframe>
<p>... facile .. oui ... lorsqu'on a les outils à disposition …</p><p>Après plusieurs déboires, j'ai enfin pu finir l'entretien de ma Kawasaki GPZ 500 S. Le but était de régler le jeu aux soupapes, entretien qui se fait tous les 12 000 kms et qui, par chance est assez facile sur ce modèle.</p>
<iframe src="https://www.youtube.com/embed/ehj21FL3new" frameborder="0" width="624" height="468"></iframe>
<p>... facile .. oui ... lorsqu'on a les outils à disposition et qu'on ne travaille pas comme un manchot.</p>
<p>J'ai éprouvé plusieurs difficultés liées aussi bien à la machine qu'au bonhomme qui a foutu ses doigts dedans (rien d'insurmontable) :</p>
<ol>
<li>vis usées qui sont impossibles à démonter sans les péter encore plus</li>
<li>joints des tuyaux d'entrée et de sortie du liquide de refroidissement fondus</li>
<li>clef à bougie inadaptée et qu'il a fallu "compenser"</li>
<li>Casse des collecteurs d'échappement en sortant la moto du garage (merci les trottoirs trop hauts)</li>
</ol>
<p>Bref, l'opération en elle-même ne réclame pas plus de 2h pour quelqu'un d'expérimenté ... dans mon cas, ça aura pris 5 jours à cause de tous les petits imprévus. (en comptant aussi les aller-retour chez le paternel pour lui piquer des outils en plus)</p>
<p>Bien sûr, j'en ai profité aussi pour changer:</p>
<ul>
<li>le filtre à air,</li>
<li>les bougies</li>
<li>le filtre à huile (+ l'huile, ça va s'en dire).</li>
</ul>
<p>Au final, malgré les embrouilles, la moto tourne maintenant comme une horloge et l'opération était loin d'être inutile.</p>
<p>Références utiles :</p>
<ul>
<li><a href="http://fr.wikipedia.org/wiki/Culasse_%28moteur%29">Page Wikipedia sur la culasse</a></li>
<li><a href="http://forum.gpzdreamteam.com/">Le forum GPZ Dream Team</a></li>
<li><a href="http://forum.gpzdreamteam.com/reglage-jeu-aux-soupapes-t13171.html">la discussion sur le forum au sujet de la vidéo</a></li>
</ul>Comment rallumer la flamme des enseignants tout en coupant dans le budget ?2011-10-22T00:00:00+02:002011-10-22T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2011-10-22:/comment-rallumer-la-flamme-des-enseignants-tout-en-coupant-dans-le-budget.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-028/chatel-mort-education.jpg"></p>
<p>L'éducation nationale s'est prise deux coups durs en très peu de temps : une <a href="http://science21.blogs.courrierinternational.com/archive/2011/10/21/suicides-d-enseignants-et-crise-de-societe-i.html">enseignante qui s'est immolée</a> devant ses élèves et un autre <a href="http://tempsreel.nouvelobs.com/societe/20111016.OBS2549/policiere-tuee-au-sabre-l-enseignant-mis-en-examen.html">enseignant qui a tué une jeune policière</a>. Dans un contexte de suppression massive de postes, on est en droit de se demander s'il n'y a pas un …</p><p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-028/chatel-mort-education.jpg"></p>
<p>L'éducation nationale s'est prise deux coups durs en très peu de temps : une <a href="http://science21.blogs.courrierinternational.com/archive/2011/10/21/suicides-d-enseignants-et-crise-de-societe-i.html">enseignante qui s'est immolée</a> devant ses élèves et un autre <a href="http://tempsreel.nouvelobs.com/societe/20111016.OBS2549/policiere-tuee-au-sabre-l-enseignant-mis-en-examen.html">enseignant qui a tué une jeune policière</a>. Dans un contexte de suppression massive de postes, on est en droit de se demander s'il n'y a pas un lien de cause à effet. Moi qui ai toujours vu le poste d'enseignant comme un des postes clefs, si ce n'est le poste clef dans notre société, je trouve ça affolant de voir par ces actes, le mal-être des enseignants actuels. J'ai la sensation profonde que durant ces dernières années, on a tué le plus beau métier du monde.</p>Huuuu Cochonne2011-09-22T00:00:00+02:002011-09-22T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2011-09-22:/huuuu-cochonne.html<p><img alt="Huuuu cochonne" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-025/huuuu_cochonne.jpg"></p>
<p>Juste un petit délire pour un ami ... et oui, je fais de temps à autre de la couleur. Comment faire quand on est daltonien ? Ben on télécharge une image sur le net qui contient la couleur qu'on pense être bonne et on l'applique à son pinceau. On fait ce qu'on …</p><p><img alt="Huuuu cochonne" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-025/huuuu_cochonne.jpg"></p>
<p>Juste un petit délire pour un ami ... et oui, je fais de temps à autre de la couleur. Comment faire quand on est daltonien ? Ben on télécharge une image sur le net qui contient la couleur qu'on pense être bonne et on l'applique à son pinceau. On fait ce qu'on peut ..</p>
<p>Un autre essai ici :</p>
<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-030/postal_missile.jpg"></p>Un framework Web python dans une bouteille2011-04-02T00:00:00+02:002011-04-02T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2011-04-02:/python-bottle-framework.html<p><a href="http://bottlepy.org/docs/dev/">Bottle</a> est un framework Web très simple
à mettre en oeuvre et qui permet d'écrire des applications Web rapides
avec peu de lignes de code. Il suffit de lire l'exemple fourni sur la
page du projet pour s'en rendre compte.</p>
<p><img alt="Introduction de Bottle" src="https://sebastien-dupire.info/pictures/0002-illustration-bottle-intro.jpg"></p>
<p>Pour illustrer son fonctionnement, nous allons créé un outil simple …</p><p><a href="http://bottlepy.org/docs/dev/">Bottle</a> est un framework Web très simple
à mettre en oeuvre et qui permet d'écrire des applications Web rapides
avec peu de lignes de code. Il suffit de lire l'exemple fourni sur la
page du projet pour s'en rendre compte.</p>
<p><img alt="Introduction de Bottle" src="https://sebastien-dupire.info/pictures/0002-illustration-bottle-intro.jpg"></p>
<p>Pour illustrer son fonctionnement, nous allons créé un outil simple de
diffusion d'un diaporama.</p>
<h2>Contexte de notre application</h2>
<p>On aimerait pouvoir diffuser un briefing (suite de diapositives) à un
ensemble de personnes via leur navigateur Web. L'idée est de réaliser
un programme qui irait lire les images dans un répertoire, prendre la
dernière uploadée dedans et l'afficher dans une page HTML.</p>
<p>On aura donc un serveur disposant :</p>
<ul>
<li>d'une application/serveur Web qui pointe sur un répertoire donné et affiche la dernière image disponible</li>
<li>d'un accès FTP sur ce même répertoire sur lequel on va placer des images de notre briefing</li>
</ul>
<p>On aura N clients qui iront sur l'URL fournie par l'application et ils
disposeront d'un bouton pour interroger régulièrement le serveur sur
l'image en cours à afficher.</p>
<p>L'administrateur déposera les fichiers au fur et à mesure sur le
répertoire accessible via FTP et l'image se mettra à jour chez les N
clients à quelques secondes près.</p>
<h2>Contraintes techniques</h2>
<p>Les clients auront une interface très simple :</p>
<blockquote>
<ul>
<li>une zone pour afficher l'image</li>
<li>un bouton leur permettant de lancer la requête AJAX qui va
interroger le serveur toutes les X secondes</li>
<li>un autre bouton pour arrêter les requêtes</li>
</ul>
</blockquote>
<p>Pour faire fonctionner tout ça, on se limitera à l'utilisation de :</p>
<ul>
<li><a href="http://bottlepy.org/docs/dev/">Bottle</a> pour l'application/serveur Web</li>
<li><a href="http://jquery.com/">jQuery</a> pour le code javascript</li>
</ul>
<h2>L'environnement de développement</h2>
<p>Pour développer dans un environnement propre de tout package inutile, on
va utiliser <a href="http://localhost">virtualenv</a></p>
<div class="highlight"><pre><span></span><code>$ mkdir briefing
$ <span class="nb">cd</span> briefing
$ pip install -U virtualenv
$ virtualenv env --no-site-packages
$ <span class="nb">source</span> env/bin/activate
<span class="o">(</span>env<span class="o">)</span>$ pip install bottle
</code></pre></div>
<h2>Le script de base</h2>
<p>Dans un premier temps, nous allons réaliser notre script de base, le
fichier <strong>serveur_bottle.py</strong></p>
<div class="highlight"><pre><span></span><code><span class="c1">## -*- coding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">bottle</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span>
<span class="nd">@bottle</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test</span><span class="p">():</span>
<span class="k">return</span> <span class="s2">"test"</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s1">'localhost'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">8080</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span><span class="o">==</span> <span class="s1">'__main__'</span> <span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre></div>
<p>On lance le script :</p>
<div class="highlight"><pre><span></span><code><span class="o">(</span>env<span class="o">)</span>$ python serveur_bottle.py
Bottle server starting up <span class="o">(</span>using WSGIRefServer<span class="o">())</span>...
Listening on http://localhost:8080/
Use Ctrl-C to quit.
</code></pre></div>
<p>Un petit tour sur <a href="http://localhost:8080">http://localhost:8080</a> et on peut voir que l'on
affiche \"test\".</p>
<p><img alt="Notre test" src="https://sebastien-dupire.info/pictures/0002_premier_test.jpg"></p>
<p>Toute l'application va fonctionner sur un principe simple : une
route(URL) \<=> une fonction qui retourne un élément (page, image, code
javascript, json, etc ...).</p>
<p>Je ne m'étendrai pas là-dessus, mais on a \"décoré\" notre fonction
<strong>test</strong> par la fonction <strong>bottle.route</strong> de façon à ce qu'elle renvoie
un resultat pour l'url de base (/).</p>
<h2>Interface de l'outil</h2>
<p>On va créer le minimum syndical pour construire notre interface :</p>
<div class="highlight"><pre><span></span><code>.
|-- css
| `-- style.css => La feuille de style CSS
|-- env
|-- images
| `-- default.jpg => l'image affichée par défaut
|-- interface.html => la page HTML contenant l'interface
`-- serveur_bottle.py
</code></pre></div>
<p>On commence par le fichier <strong>interface.html</strong></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"Content-type"</span> <span class="na">content</span><span class="o">=</span><span class="s">"text/html; charset=utf-8"</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/css/style.css"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/css"</span> <span class="na">media</span><span class="o">=</span><span class="s">"screen"</span> <span class="na">charset</span><span class="o">=</span><span class="s">"utf-8"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>Outil de Briefing<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>Mon outil de Briefing<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
<span class="cm"><!-- Contiendra notre image --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"img-container"</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">><</span><span class="nt">img</span> <span class="na">id</span><span class="o">=</span><span class="s">"current-diapo"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/images/default.jpg"</span> <span class="na">title</span><span class="o">=</span><span class="s">"briefing current img"</span> <span class="p">/></</span><span class="nt">p</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span><span class="cm"><!-- img-container --></span>
<span class="cm"><!-- Contiendra nos boutons d'actions --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"actions"</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">""</span> <span class="na">title</span><span class="o">=</span><span class="s">"start briefing"</span><span class="p">></span> Start Briefing<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">""</span> <span class="na">title</span><span class="o">=</span><span class="s">"start briefing"</span><span class="p">></span> Stop Briefing<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span><span class="cm"><!-- actions --></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>Puis, on continue avec le fichier <strong>styles.css</strong></p>
<div class="highlight"><pre><span></span><code><span class="p">.</span><span class="nc">img-container</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">width</span><span class="p">:</span><span class="w"> </span><span class="mi">640</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">height</span><span class="p">:</span><span class="w"> </span><span class="mi">480</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">border</span><span class="p">:</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">black</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="nt">p</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="kt">px</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<h2>Interaction avec Bottle</h2>
<p>On va modifier notre script de base pour utiliser notre interface html</p>
<div class="highlight"><pre><span></span><code><span class="c1">## -*- coding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">bottle</span>
<span class="nd">@bottle</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">send_file</span><span class="p">(</span><span class="n">root</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span>
<span class="n">filename</span><span class="o">=</span><span class="s1">'interface.html'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s1">'localhost'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">8080</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span><span class="o">==</span> <span class="s1">'__main__'</span> <span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre></div>
<p>On lance le serveur et on retourne sur l'adresse
<a href="http://localhost:8080">http://localhost:8080</a></p>
<p><img alt="Interface mais il y a un soucis" src="https://sebastien-dupire.info/pictures/0002_interface_sans_css.jpg"></p>
<p>On a un problème : ni le CSS, ni l'image ne sont pris en compte.
Rendez-vous sur les liens suivants pour bien s'en rendre compte :</p>
<ul>
<li><a href="http://localhost:8080/css/style.css">http://localhost:8080/css/style.css</a></li>
<li><a href="http://localhost:8080/images/default.jpg">http://localhost:8080/images/default.jpg</a></li>
</ul>
<p>Le soucis est que Bottle ne sait pas comment gérer ces deux URLs. Il
faut lui indiquer comment traiter <strong>/css/</strong> et <strong>/images/</strong>.</p>
<h2>Ajout de répertoires statiques</h2>
<p>On va indiquer à Bottle comment retourner les images et les css en
ajoutant respectivement une fonction <strong>render_image</strong> et une fonction
<strong>render_css</strong> que l'on va \"router\" de la manière suivante :</p>
<div class="highlight"><pre><span></span><code><span class="nd">@bottle</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/css/:filename'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">render_css</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">send_file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span> <span class="s1">'css'</span><span class="p">))</span>
<span class="nd">@bottle</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/images/:filename'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">render_image</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">send_file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span> <span class="s1">'images'</span><span class="p">))</span>
</code></pre></div>
<p>Si on va sur <a href="http://localhost:8080/images/default.jpg">http://localhost:8080/images/default.jpg</a>, la fonction va
nous retourner le fichier <strong>default.jpg</strong> du répertoire <strong>/images/</strong> qui
est contenu dans le répertoire courant.</p>
<p>Si on relance le serveur, on obtient bien notre interface avec notre
image et notre CSS.</p>
<p><img alt="Interface complète...moche mais complète" src="https://sebastien-dupire.info/pictures/0002_interface_avec_css.jpg"></p>
<p>On pourra faire la même chose avec un répertoire <strong>/js/</strong> qui contiendra
nos fichiers javascript. Mais nous avons autant définir une route plus
générique.</p>
<div class="highlight"><pre><span></span><code><span class="nd">@bottle</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/:dirname/:filename'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">render_files</span><span class="p">(</span><span class="n">dirname</span><span class="p">,</span> <span class="n">filename</span><span class="p">):</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">send_file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span> <span class="n">dirname</span><span class="p">))</span>
</code></pre></div>
<h2>Utilisation d'un template</h2>
<p>Admettons maintenant qu'on veut passer des paramètres à notre interface
comme :</p>
<ul>
<li>le titre de l'outil</li>
<li>et/ou l'image par défaut</li>
<li>et/ou encore une zone accessible qu'en mode d'administrateur.</li>
</ul>
<p>On utilisait <strong>bottle.send_file</strong> pour retourner notre fichier html.
Bottle peut utiliser ce fichier <strong>interface.html</strong> comme un template via
la fonction <strong>bottle.template</strong>.</p>
<p>On modifie donc notre fonction <strong>index</strong></p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
<span class="n">template_file</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span> <span class="s1">'interface.html'</span><span class="p">)</span>
<span class="k">return</span> <span class="n">bottle</span><span class="o">.</span><span class="n">template</span><span class="p">(</span><span class="n">template_file</span><span class="p">,</span>
<span class="n">title</span><span class="o">=</span><span class="s2">"Briefing Tools"</span><span class="p">,</span>
<span class="n">default_image</span><span class="o">=</span><span class="s2">"/images/default.jpg"</span><span class="p">,</span>
<span class="n">admin</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</code></pre></div>
<p>ainsi que le template <strong>interface.html</strong> pour utiliser ces nouveaux
paramètres</p>
<div class="highlight"><pre><span></span><code><span class="cp"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"Content-type"</span> <span class="na">content</span><span class="o">=</span><span class="s">"text/html; charset=utf-8"</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/css/style.css"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/css"</span> <span class="na">media</span><span class="o">=</span><span class="s">"screen"</span> <span class="na">charset</span><span class="o">=</span><span class="s">"utf-8"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>{{title}}<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>{{title}}<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
<span class="cm"><!-- Contiendra notre image --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"img-container"</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">><</span><span class="nt">img</span> <span class="na">id</span><span class="o">=</span><span class="s">"current-diapo"</span> <span class="na">src</span><span class="o">=</span><span class="s">"{{default_image}}"</span> <span class="na">title</span><span class="o">=</span><span class="s">"briefing current img"</span> <span class="p">/></</span><span class="nt">p</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span><span class="cm"><!-- img-container --></span>
% if admin == True:
<span class="cm"><!-- Contiendra nos boutons d'actions --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"actions"</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">""</span> <span class="na">title</span><span class="o">=</span><span class="s">"start briefing"</span><span class="p">></span> Start Briefing<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">""</span> <span class="na">title</span><span class="o">=</span><span class="s">"start briefing"</span><span class="p">></span> Stop Briefing<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span><span class="cm"><!-- actions --></span>
% end
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<h2>Scanner les répertoires</h2>
<p>Ajoutons maintenant un répertoire sur lequel pointera notre FTP.
Appelons le <strong>/ftp/</strong>.</p>
<p>Quand un client appuira sur \"Start Briefing\", cela lancera une requête
AJAX toutes les trois secondes afin de demander au serveur, la dernière
image uploadée disponible dans le répertoire <strong>/ftp/</strong>. Il suffira de
cliquer sur \"Stop Briefing\" pour interrompre ces requêtes
automatiques.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"Content-type"</span> <span class="na">content</span><span class="o">=</span><span class="s">"text/html; charset=utf-8"</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/css/style.css"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/css"</span> <span class="na">media</span><span class="o">=</span><span class="s">"screen"</span> <span class="na">charset</span><span class="o">=</span><span class="s">"utf-8"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>{{title}}<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/javascript"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/js/jquery-1.5.2.min.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/javascript"</span><span class="p">></span>
<span class="kd">function</span> <span class="nx">refresh</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/getdata'</span> <span class="p">,</span> <span class="p">{</span>
<span class="s1">'action'</span><span class="o">:</span> <span class="s1">'refresh'</span><span class="p">,</span>
<span class="p">},</span>
<span class="kd">function</span> <span class="nx">success</span><span class="p">(</span><span class="nx">reponse</span><span class="p">){</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">timeout_flag</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">reponse</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="s1">'OK'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s1">'#current-diapo'</span><span class="p">)[</span><span class="mf">0</span><span class="p">].</span><span class="nx">src</span> <span class="o">=</span> <span class="s1">'/ftp/'</span> <span class="o">+</span> <span class="nx">reponse</span><span class="p">.</span><span class="nx">last_file</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s1">'#current-diapo'</span><span class="p">)[</span><span class="mf">0</span><span class="p">].</span><span class="nx">src</span> <span class="o">=</span> <span class="s1">'{{default_image}}'</span>
<span class="p">}</span>
<span class="nx">timeout_flag</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> <span class="nx">refresh</span><span class="p">();</span> <span class="p">},</span> <span class="mf">3000</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="s2">"json"</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">start</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">timeout_flag</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> <span class="nx">refresh</span><span class="p">();</span> <span class="p">},</span> <span class="mf">100</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">stop</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timeout_flag</span><span class="p">)</span>
<span class="p">}</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>{{title}}<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
<span class="cm"><!-- Contiendra notre image --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"img-container"</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">><</span><span class="nt">img</span> <span class="na">id</span><span class="o">=</span><span class="s">"current-diapo"</span> <span class="na">src</span><span class="o">=</span><span class="s">"{{default_image}}"</span> <span class="na">title</span><span class="o">=</span><span class="s">"briefing current img"</span> <span class="p">/></</span><span class="nt">p</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span><span class="cm"><!-- img-container --></span>
% if admin == True:
<span class="cm"><!-- Contiendra nos boutons d'actions --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"actions"</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"#"</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"start(); return false;"</span> <span class="na">title</span><span class="o">=</span><span class="s">"start briefing"</span><span class="p">></span> Start Briefing<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"#"</span> <span class="na">onclick</span><span class="o">=</span><span class="s">"stop(); return false;"</span><span class="p">></span> Stop Briefing<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span><span class="cm"><!-- actions --></span>
% end
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>Coté serveur, on ajoute :</p>
<ul>
<li>une route pour obtenir le nom de la dernière image uploadée dans le répertoire.</li>
<li>une liste qui se met à jour en fonction des fichiers présents.</li>
<li>une vérification de fichiers très sommaire qui ajoute à la liste la dernière image trouvée que si sa date de dernière modification n'évolue pas pendant 0.5 secondes. Ceci permettra de faire un test rapide pour vérifier que l'image est complétement uploadée sur le FTP (ce n'est pas la bonne manière, mais pour l'exemple, ça marche).</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="nd">@bottle</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/getdata'</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s1">'post'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_data</span><span class="p">():</span>
<span class="k">global</span> <span class="n">files_list</span>
<span class="k">if</span> <span class="n">bottle</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">POST</span><span class="p">[</span><span class="s1">'action'</span><span class="p">]</span><span class="o">==</span><span class="s1">'refresh'</span><span class="p">:</span>
<span class="k">for</span> <span class="n">root</span><span class="p">,</span> <span class="n">dirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span> <span class="s1">'ftp'</span><span class="p">)):</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
<span class="k">if</span> <span class="n">filename</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">files_list</span><span class="p">:</span>
<span class="n">mtime_before_sleep</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">getmtime</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
<span class="n">mtime_after_sleep</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">getmtime</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span>
<span class="k">if</span> <span class="n">mtime_after_sleep</span> <span class="o">==</span> <span class="n">mtime_before_sleep</span><span class="p">:</span>
<span class="n">files_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files_list</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span> <span class="s1">'ftp'</span><span class="p">,</span> <span class="n">filename</span><span class="p">)):</span>
<span class="n">files_list</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">files_list</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">"status"</span><span class="p">:</span> <span class="s2">"OK"</span><span class="p">,</span>
<span class="s2">"last_file"</span><span class="p">:</span> <span class="n">files_list</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">"status"</span><span class="p">:</span> <span class="s2">"NO_IMAGE"</span><span class="p">}</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="k">global</span> <span class="n">files_list</span>
<span class="n">files_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">bottle</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s1">'localhost'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">8080</span><span class="p">)</span>
</code></pre></div>
<p>A noter que notre fonction retourne un dictionnaire qui sera transformé
automatiquement au format json par bottle.</p>
<h2>Utilisation de l'outil</h2>
<p>On peut lancer la démonstration.</p>
<p>1- On ouvre notre répertoire FTP : une copie d'un fichier image vers ce
répertoire permettra de simuler un transfert vers un compte FTP (en plus
rapide).</p>
<p>2- On ouvre notre outil sur <a href="http://localhost:8080">http://localhost:8080</a> et on clique sur
\"Start Briefing\".</p>
<p>3- On copie les images une par une dans le répertoire.</p>
<p>L'application va alors rafraichir l'image au fur et à mesure comme
présenté sur l'animation ci-dessous.</p>
<p><img alt="Animation de l'utilisation de l'application" src="https://sebastien-dupire.info/pictures/0002_testinterface.gif"></p>
<h2>Conclusions</h2>
<p>Bottle est très simple à utiliser et à mettre oeuvre. Il utilise un
système de routage via des décorateurs (les @ au dessus de chaque
fonction) et un système de template. Pour de petits projets, cela
peut-être un moyen rapide d'obtenir quelque chose de fonctionnel avec
la puissance du langage Python derrière.</p>
<p>Cet exemple codé avec les pieds, n'est qu'un essai à titre
démonstratif. Vous pouvez le télécharger en <a href="files/0002_briefing.zip">cliquant
ici</a>.</p>
<p>L'exemple est inspiré de l'outil <strong>loform_briefing_tools</strong> que j'ai
développé pour remplir ce besoin simple. Il est disponible à l'adresse
suivante : (https://bitbucket.org/kyoku57/loform_briefing_tools/src)</p>
<p>Il dispose d'une interface d'admin facilement accessible afin de
pouvoir choisir l'image à diffuser à l'ensemble des clients connectés
à l'application, de classer les images par taille, par nom et par date
de dernière modification. Chaque client peut interrompre le briefing
pour revenir à une image précédente. Et enfin, le tout s'utilise sans
base de données.</p>
<p><img alt="Conclusions sur Bottle" src="https://sebastien-dupire.info/pictures/0002-illustration-bottle-conclusion.jpg"></p>
<h3>Références</h3>
<ul>
<li><a href="http://bottlepy.org">Le site officiel de Bottle</a></li>
<li><a href="http://pypi.python.org/pypi/virtualenv">Virtualenv documentation</a></li>
</ul>Découverte de Pelican2011-03-02T00:00:00+01:002011-03-02T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-03-02:/decouverte-de-pelican.html<p>Pelican est un générateur de sites statiques HTML à partir de fichiers de différents format comme le reStructuredText (.rst), le Markdown (.md) ou encore le HTML (.html).</p>
<p><img alt="Qu'est ce que Pélican" src="https://sebastien-dupire.info/pictures/0001-illustration-pelican.jpg"> </p>
<h2>Un blog, c'est statique ou dynamique ?</h2>
<h3>Qu'est ce qu'un Weblog finalement ?</h3>
<p>Un Weblog, ou blog, C'est un journal diffusé sur Internet composé :</p>
<ul>
<li>d'articles …</li></ul><p>Pelican est un générateur de sites statiques HTML à partir de fichiers de différents format comme le reStructuredText (.rst), le Markdown (.md) ou encore le HTML (.html).</p>
<p><img alt="Qu'est ce que Pélican" src="https://sebastien-dupire.info/pictures/0001-illustration-pelican.jpg"> </p>
<h2>Un blog, c'est statique ou dynamique ?</h2>
<h3>Qu'est ce qu'un Weblog finalement ?</h3>
<p>Un Weblog, ou blog, C'est un journal diffusé sur Internet composé :</p>
<ul>
<li>d'articles, de catégories, de mots-clefs</li>
<li>d'une navigation pour parcourir les articles,</li>
<li>ou d'un autre moyen pour y accéder : moteur de recherche, nuage de mots-clefs, arbres, ...</li>
</ul>
<p>On peut y ajouter pleins de choses autour qui transformeraient vite un simple blog en sapin de noël : les goûts et les couleurs ...</p>
<p>Donc, si on fait le bilan, tout ce beau monde peut être :</p>
<ul>
<li>soit classé dans une base de données : on utilisera alors des scripts (PHP, Python, Ruby) pour y accéder à chaque requête et pour les mettre en forme,</li>
<li>soit classé sur le filesystem : on utilise des scripts lors des requêtes HTTP pour convertir les fichiers en page html,</li>
<li>soit classé sur le filesystem : on consulte directement des pages HTML qui auront été préalablement converties depuis d'autres fichiers.</li>
</ul>
<p>C'est bien entendu sur cette dernière option que Pelican se place. </p>
<h3>Mais euh, c'est statique tout ça !</h3>
<p>Si vous n'écrivez qu'un article par jour (ou par semaine), est-ce nécessaire que le serveur aille à chaque requête, récupérer les données d'une base de données, les mettre en forme via des scripts pour restituer la page finale ? Même dans le cas de l'utilisation d'un cache, vous allez devoir gérer ce cache, le générer au besoin et ça aura une influence sur la consommation en mémoire de votre serveur.</p>
<p>Tout ça pour quoi finalement ? Pour afficher des articles au contenu fixe avec les mêmes urls. Ils seront classés dans les mêmes catégories, avec les mêmes mots-clefs. Au final, on aurait pu très bien afficher des pages statiques dès le départ qui éventuellement changeraient que s'il y avait un réel changement. Les menus peuvent être faits "en dur", les mots-clefs aussi sauf qu'effectivement, si on a plusieurs articles, il faudrait les menus sur l'ensemble des articles.</p>
<p>Pour résumer, un Weblog tel qu'il est décrit au dessus, n'est qu'un ensemble de pages statiques qui peuvent être occasionnellement mises à jour. Le plus dur étant la mise à jour de la navigation. Mais ça, ce sera justement le travail de Pelican : il générera la navigation à chaque fois qu'on ajoutera un article.</p>
<h3>Ah oui, mais les commentaires et le moteur de recherche, c'est dynamique non ?</h3>
<p>C'est pas faux mais, il existe des alternatives :</p>
<ul>
<li>le moteur de recherche peut être externalisé : Google permet de faire des recherches uniquement sur son site</li>
<li>pour les commentaires, Pelican est compatible avec <code>Disqus <http://disqus.com/></code>_, un service externalisé de commentaires. Pelican a déjà des options pour utiliser ce service.</li>
</ul>
<p>Pourquoi refaire ce qui est mieux fait chez les autres ? Si en plus c'est disponible, gratuit, fonctionnel, et qu'on peut récupérer ses données d'une façon ou d'une autre, je n'y vois rien de pénalisant.</p>
<h2>Avantages, désavantages d'un site statique</h2>
<h3>Avantages</h3>
<ul>
<li>les besoins serveurs sont faibles : pas de script coté serveur, donc on économise la RAM, les ressources processeurs et la bande passante,</li>
<li>l'accès est très rapide : il n'y a pas de script coté serveur, juste du contenu statique téléchargé,</li>
<li>la sécurité est haute : encore une fois, rien n'est executé coté serveur, donc pas de risque d'avoir des trous de sécurité.</li>
<li>rien n'empêche d'ajouter du javascript dans les templates du thème afin de "dynamiser" son blog.</li>
</ul>
<h3>Désavantages</h3>
<ul>
<li>il faut regénérer l'ensemble du site à chaque modification (et encore, c'est en cours de changement ça),</li>
<li>tout module dynamique complémentaire doit être externalisé au blog.</li>
</ul>
<h2>Utilisation de Pelican</h2>
<p>Pour générer un blog à partir de simples fichiers textes, nous allons utiliser pelican. C'est lui qui se chargera de créer l'ensemble des pages, des menus et des liens du notre blog.</p>
<p>Pour cela, vous devez avoir <strong>python</strong> et <strong>pip</strong> installés sur votre poste.</p>
<p>Ensuite, il suffit de faire :</p>
<div class="highlight"><pre><span></span><code>$ pip install pelican
</code></pre></div>
<p>Vous créez un dossier <strong>mon_site</strong> dans lequel vous placez un fichier <strong>mon_article.rst</strong> avec le contenu suivant :</p>
<div class="highlight"><pre><span></span><code>:date: 2011-03-02
:title: Mon premier article
:slug: mon-premier-article
:category: Mes articles
:tags: pelican, article, essai
Bonjour
=======
Ceci est mon prier article sous Pelican. La classe non ?
</code></pre></div>
<p>C'est grâce à ces métas-données en tête de fichier, que Pelican va pouvoir construire les pages et menus correspondants aux différentes catégories du site. Une fois enregistré, vous pouvez lancer la génération du blog avec Pelican :</p>
<div class="highlight"><pre><span></span><code>$ pelican mon_site/ -o mon_site_html/
</code></pre></div>
<p>La génération du HTML se fait rapidement:</p>
<div class="highlight"><pre><span></span><code><span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/feeds/all.atom.xml
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/feeds/Mes articles.atom.xml
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/feeds/all-en.atom.xml
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/mon-premier-article.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/index.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/tags.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/categories.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/archives.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/tag/essai.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/tag/pelican.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/tag/article.html
<span class="o">[</span>ok<span class="o">]</span> writing /home/sebastien/Public/mon_site_html/category/Mes articles.html
<span class="o">[</span>ok<span class="o">]</span> copying /home/sebastien/Public/pelican-skel/resources/pelican/pelican/themes/notmyidea/static to /home/sebastien/Public/mon_site_html/theme/.
</code></pre></div>
<p>On voit qu'il a créé une page pour les catégories, pour les archives, les flux RSS et les tags, en plus de l'article lui même.</p>
<p>Après génération du blog, on obtient le site suivant via son navigateur:</p>
<p><img alt="mon premier site sous pelican" src="https://sebastien-dupire.info/pictures/0001-premier-site-sous-pelican.jpg"></p>
<p>Après, il est possible de faire des pages en plus des articles, de rajouter les commentaires (via disqus par exemple), d'afficher les catégories autrement, afficher les tags, créer vos propres thèmes, etc ...</p>
<p>Le mieux est de visiter les liens ci-dessous afin de vous familiariser avec la syntaxe à employer et les options disponibles. Il y a déjà de quoi bien s'amuser.</p>
<h2>Liens utiles</h2>
<ul>
<li><a href="http://docs.notmyidea.org/alexis/pelican/">le site dédié du projet</a></li>
<li><a href="http://github.com/ametaireau/pelican/">le dépot officel sous Github</a></li>
<li><a href="http://linuxfr.org/news/pelican-un-g%C3%A9n%C3%A9rateur-de-blog-statique">la news dans linuxfr</a></li>
<li><a href="http://docutils.sourceforge.net/rst.html">syntaxe reStructuredText</a></li>
</ul>Tubulure2011-02-05T00:00:00+01:002011-02-05T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-02-05:/tubulure.html<p><img alt="Tubulure" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-006/bdamateur_tubulure.jpg" title="Tubulure"></p>
<p>Essai pour le dessin de la semaine sur <a href="http://www.bdamateur.com">bdamateur.com</a>. Le mot de la semaine était "TUBULURE"</p>FDN et Google fournissent de quoi communiquer sur Internet en Egypte2011-02-01T00:00:00+01:002011-02-01T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-02-01:/fdn-et-google-fournissent-de-quoi-communiquer-sur-internet-en-egypte.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-005/egypte_telephone_arabe.jpg" title="FDN et Google fournissent de quoi communiquer sur Internet en Egypte"></p>
<p>Liens :</p>
<ul>
<li><a href="http://blog.fdn.fr/post/2011/01/28/Censure-de-l-internet-en-%C3%89gypte-%3A-une-humble-action-de-FDN">Blog FDN sur la censure en Egypte</a></li>
<li><a href="http://www.lemonde.fr/proche-orient/article/2011/02/01/google-lance-un-twitter-contre-la-censure-en-egypte_1473336_3218.html">Le Monde - service contre censure</a></li>
</ul>Alien versus Vitriol2011-01-30T00:00:00+01:002011-01-30T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2011-01-30:/alien-versus-vitriol.html<p><img alt="image" class="aligncenter" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/HS-003/bd_amateur_semaine_du_vitriole.png"></p>
<p>Planche créée pour le dessin de la semaine sur <a href="http://www.bdamateur.com" title="Site Internet de BD Amateur">bdamateur.com</a> qui avait pour thème le mot : <a href="http://fr.wikipedia.org/wiki/Vitriol" title="Définition du Vitriol sur Wikipedia">VITRIOL</a></p>Remontage des carburateurs sur GPZ 500 S2010-08-16T00:00:00+02:002019-12-14T00:00:00+01:00Sébastien Dupiretag:sebastien-dupire.info,2010-08-16:/demontage-remontage-carburateur-sur-gpz-500-s.html<p>La GPZ est une moto relativement facile à entretenir au niveau mécanique (au niveau de l'électricité c'est autre chose...). Ici, je vous propose une vidéo du comment remonter la rampe de carburateur avec en prime l'utilisation d'un dépressio-mètre.</p>
<p>Je remercie encore les gens du forum <a href="https://forum.gpzdreamteam.com">GPZ Dream Team</a> pour leurs …</p><p>La GPZ est une moto relativement facile à entretenir au niveau mécanique (au niveau de l'électricité c'est autre chose...). Ici, je vous propose une vidéo du comment remonter la rampe de carburateur avec en prime l'utilisation d'un dépressio-mètre.</p>
<p>Je remercie encore les gens du forum <a href="https://forum.gpzdreamteam.com">GPZ Dream Team</a> pour leurs aides diverses et variées.</p>
<iframe src="https://player.vimeo.com/video/14187506" width="624" height="468" frameborder="0" allow="fullscreen" allowfullscreen></iframe>
<p>Références utiles :</p>
<ul>
<li><a href="http://www.amazon.fr/Revue-technique-Moto-num%C3%A9ro-76-1/dp/2726890709/ref=sr_1_1?ie=UTF8&s=books&qid=1281979256&sr=8-1">la Revue Moto Technique</a></li>
<li><a href="http://forum.gpzdreamteam.com/">Le forum GPZ Dream Team</a></li>
<li><a href="https://forum.gpzdreamteam.com/montage-reglage-rapide-des-carburateurs-t10725.html">la discussion sur le forum au sujet de la vidéo</a></li>
<li><strong>EDIT 14/12/2019</strong> : <a href="https://www.youtube.com/watch?v=nqZrRJK5r-Y">la vidéo sur Youtube</a> - à priori ma vidéo a eu du succès et quelqu'un l'a reprise et diffusé sur Youtube en 2017 .. 7 ans plus tard donc. Marrant ...</li>
</ul>Création d'un dépressiomètre2010-08-14T00:00:00+02:002010-08-14T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2010-08-14:/creation-du-comparateur-de-depression.html<p>Avant de remonter les carburateurs, il fallait que je me fasse un comparo-dépressiomètre. Pour cela, rien de plus simple : quelques tubes, du liquide visqueux (SAE 15W50) et un peu de bricolage. L'idée a été trouvée sur le lien suivant : <a href="http://dnepr.ural.free.fr/depressiometre.htm">http://dnepr.ural.free.fr/depressiometre.htm</a> que je vous invite …</p><p>Avant de remonter les carburateurs, il fallait que je me fasse un comparo-dépressiomètre. Pour cela, rien de plus simple : quelques tubes, du liquide visqueux (SAE 15W50) et un peu de bricolage. L'idée a été trouvée sur le lien suivant : <a href="http://dnepr.ural.free.fr/depressiometre.htm">http://dnepr.ural.free.fr/depressiometre.htm</a> que je vous invite à visiter, c'est un site riche en technique pour régler votre moto (surtout les vieilles russes).</p>
<p>Le but est simple : chaque durit sera branchée à un carburateur. Quand le moteur sera en route, le liquide va se mettre à osciller. Le but du jeu sera de régler la vis de synchronisation des carburateurs de façon à ce que les deux niveaux de liquide soit quasiment identiques.</p>
<p>Voici la bête de face. On voit bien le niveau de liquide, le tuyau cristal utilisé et les embranchements pour les durites. J'ai créé à l'occasion deux tuyaux identifiables par un marquage, afin de pouvoir les câbler sur les carburateurs avec un peu de marge.</p>
<p><img alt="depressiomètre vu de face" src="https://sebastien-dupire.info/pictures/front_depressio.jpg"></p>
<p>Voici la bête vu de l'arrière. On voit un petit réservoir (on dirait une perfusion) dans lequel je mettrai l'essence car je ne peux pas la tirer depuis le réservoir, vu que la prise de dépression sera prise pour le dépressiomètre. Et juste en dessous, ça me permet de ranger le câble de vidange des cuves.</p>
<p><img alt="depressiomètre vu de l'arrière" src="https://sebastien-dupire.info/pictures/rear_depressio.jpg"></p>
<p>Ensuite, on attaque le remontage des carburateurs et, avec un peu de chance, les réglages de ces bestiaux grâce au comparo-dépressiomètre fraichement monté.</p>
<p>Prix du bouzin ? 20 € chez Leroy-Merlin ... à comparer avec les 80 € d'un dépressiomètre à aiguille, le fun en moins.</p>Remontage du vélo électrique ISD 6012010-07-25T00:00:00+02:002010-07-25T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2010-07-25:/remontage-du-velo-electrique-isd-601.html<p>Nous y voilà : deuxième partie de la réparation du vélo. Tout d'abord, après avoir tout démonté, on va retirer le cable de la poignée Grip Shift du dérailleur arrière. Pour cela, rien de plus simple, on trouve le plomb, on le dégage avec un tournevis fin et on tire. Le …</p><p>Nous y voilà : deuxième partie de la réparation du vélo. Tout d'abord, après avoir tout démonté, on va retirer le cable de la poignée Grip Shift du dérailleur arrière. Pour cela, rien de plus simple, on trouve le plomb, on le dégage avec un tournevis fin et on tire. Le nouveau cable se glisse dans l'orifice, et contrairement à ce que je pensais, c'était un jeu d'enfant.</p>
<p><img alt="image1" src="https://sebastien-dupire.info/pictures/04_levier_vitesse.jpg"></p>
<p>On recoupe toutes les gaines du cable d'origine à la bonne taille, et il n'y a plus qu'à faire parcourir ce cable le long du cadre.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/05_levier_embout.jpg"></p>
<p>Ensuite, on nettoie le dérailleur arrière ainsi que le changeur de plateau. J'ai utilisé des chiffons, de l'eau légèrement mélangé avec du liquide vaisselle, et beaucoup d'huile de coude.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/07_nettoyage_vitesse.jpg"></p>
<p>On sort la chaine du cadre en retirant un maillon qui sera remplacé par une attache rapide. On en profite pour nettoyer, degraisser la chaine : eau + liquide vaisselle toujours + coca.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/08_separtion_chaine.jpg"></p>
<p>L'attache rapide en question, est tout ce qui a de plus standard acheté 3 € chez Decathlon.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/09_attache_rapide.jpg"></p>
<p>Au remontage de la gaine sur le cadre, il ne faut pas oublier de graisser l'intérieur.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/10_graissage_recablage.jpg"></p>
<p>On remonte le dérailleur arrière. Une seule vis, et le cable est facile à remonter aussi. On s'occupera des réglages plus tard.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/11_derailleur_arriere.jpg"></p>
<p>Idem avec le changeur de plateau, qu'on replace ainsi que son cable.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/12_vitesse_plateau.jpg"></p>
<p>On inspecte les freins arrières et avant. Après un brossage en règle, il ne reste plus qu'à les disposer sur le cadre.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/13_frein_arriere_preparation.jpg"></p>
<p>Les voicis en place après serrage.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/15_remise_frein_arriere.jpg"></p>
<p>Idem à l'avant, ou après ça, on peut remettre la roue et les poignets sur le guidon.</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/16_remise_roues.jpg"></p>
<p>Je passe le détail du remontage de la roue arrière, c'est du grand classique. Et voilà le résultat après avoir tout reboulonner, tout resserrer et mis les gardes boues d'origines. C'est quand même mieux non ?</p>
<p><img alt="image" src="https://sebastien-dupire.info/pictures/17_velo_complet.jpg"></p>
<p>Je l'utilise de nouveau depuis un mois environ, et c'est un vrai bonheur...j'avais oublié que le vélo tractait aussi fort.</p>
<h2>Que reste-t-il à faire ?</h2>
<ul>
<li>Je ne me suis pas occupé des rayons arrières. La roue arrière est suffisamment rigide pour rester droite avec trois rayons en moins</li>
<li>Je ne me suis pas occupé de la suspension avant. Pareil, elle tient bien le choc, mais il faudra changer les soufflets à l'occasion</li>
<li>Je ne me suis pas occupé de la liaison pivot de la partie arrière du vélo. Après une pluie, ça grince un peu. Rien de grave mais quand j'aurais le temps.</li>
<li>pas de changement de batterie, le vélo fait 30 km chargé. Tant que je ne descend pas en dessous de 20, je continue avec ce pack batterie (faut compter 180 € pour un neuf au plomb).</li>
</ul>Twilight - Le trio impossible2010-07-09T00:00:00+02:002010-07-09T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2010-07-09:/twilight-le-trio-impossible.html<p><img alt="image" src="https://sebastien-dupire.info/ArtsBD/HorsSeries/humour/twilight.jpg"></p>Remise en état du vélo électrique2010-06-15T00:00:00+02:002010-06-15T00:00:00+02:00Sébastien Dupiretag:sebastien-dupire.info,2010-06-15:/remise-en-etat-du-velo-electrique-part-1.html<p>Le vélo électrique a déjà deux ans et malheureusement, a beaucoup subi les intérempéries. Ceci est lié au fait qu'il était la plupart du temps attaché sur un parking ouvert dans Metz. Résultat, en place d'avoir un vélo brillant et fonctionnel, on se retrouve deux ans plus tard, avec un …</p><p>Le vélo électrique a déjà deux ans et malheureusement, a beaucoup subi les intérempéries. Ceci est lié au fait qu'il était la plupart du temps attaché sur un parking ouvert dans Metz. Résultat, en place d'avoir un vélo brillant et fonctionnel, on se retrouve deux ans plus tard, avec un amas de métal et de caoutchouc qui frottent plus qu'autre chose et dont chaque élément explose semaine après semaine (frein, pneu, câbles, etc ...). Après une ultime crevaison, il était temps de le remettre en l'état. L'opération va donc consister à tout démonter, à tout vérifier, changer ce qui est pété et tout remonter (le plus dur).</p>
<p><img alt="Vélo au bloc opératoire" src="https://sebastien-dupire.info/pictures/velo_1.jpg"></p>
<p>Le nettoyage va consister en plusieurs étapes :</p>
<ul>
<li>nettoyage des roues</li>
<li>démontage de la chaine et nettoyage du dérailleur</li>
<li>démontage des plateaux</li>
<li>démontage de l'axe pivot et de la suspension arrière</li>
<li>nettoyage du cadre + graissage de l'axe pivot</li>
<li>remplacement des soufflet de la suspension avant</li>
<li>changer le câble du dérailleur arrière</li>
<li>tout remonter et régler les vitesses</li>
</ul>
<h2>La remise en état des deux roues.</h2>
<p>Le constat n'est pas brillant. La roue est bourrée de graisse, le pneu est usé jusqu'à la corde, et la chambre à air est complétement trouée. Démontage de la roue arrière s'impose, et on sort la brosse à nettoyer avec de l'eau (pas de produit pour éviter d'abimés les joints).</p>
<p><img alt="La roue arrière, avant nettoyage" src="https://sebastien-dupire.info/pictures/velo_2.jpg"></p>
<p>Quelques minutes plus tard ...</p>
<p><img alt="Roue arrière, après nettoyage" src="https://sebastien-dupire.info/pictures/velo_3.jpg"></p>
<p>Et on recommence avec la roue avant.</p>
<p><strong>Coût de l'opération</strong> : 46 €</p>
<ul>
<li>2 pneus chez Decathlon : 2 x 15 €</li>
<li>2 bandes de jantes : 2 x 3 €</li>
<li>2 chambres à air : 2 x 5 €</li>
</ul>